deepchart后台管理系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

681 lines
17 KiB

  1. <template>
  2. <div>
  3. <div class="page-container" v-show="taber == 'DeepMate'">
  4. <!-- 搜索区域 -->
  5. <div class="search-container">
  6. <div class="search-form">
  7. <div class="search-item">
  8. <span class="form-label">账号</span>
  9. <el-input
  10. v-model="searchFormDM.dccode"
  11. placeholder="请输入账号"
  12. clearable
  13. style="height: 36px; width: 140px;"
  14. />
  15. </div>
  16. <div class="search-item">
  17. <span class="form-label">姓名</span>
  18. <el-input
  19. v-model="searchFormDM.dcname"
  20. placeholder="请输入姓名"
  21. clearable
  22. style="height: 36px; width: 180px;"
  23. />
  24. </div>
  25. <div class="search-item">
  26. <span class="form-label">客户类型</span>
  27. <el-select
  28. v-model="searchFormDM.user_role"
  29. placeholder="请选择客户类型"
  30. clearable
  31. style="height: 36px; width: 160px;"
  32. >
  33. <el-option label="非网" value="2" />
  34. <el-option label="会员" value="1" />
  35. </el-select>
  36. </div>
  37. <div class="search-item">
  38. <span class="form-label">地区</span>
  39. <el-select
  40. v-model="searchFormDM.market"
  41. placeholder="请选择地区"
  42. clearable
  43. style="height: 36px; width: 160px;"
  44. :loading="isRegionLoading"
  45. >
  46. <el-option
  47. v-for="region in regionList"
  48. :key="region.id"
  49. :label="region.text_cn"
  50. :value="region.market"
  51. />
  52. </el-select>
  53. </div>
  54. <div class="button-group">
  55. <el-button type="primary" @click="searchDM">搜索</el-button>
  56. <el-button type="danger" @click="enableAccessDM">开通权限</el-button>
  57. <el-button type="success" @click="exportExcelDM">导出Excel列表</el-button>
  58. <el-button color="#626aef" @click="exportListDM">查看导出列表</el-button>
  59. <el-button type="primary" @click="resetBnDM">重置</el-button>
  60. </div>
  61. </div>
  62. </div>
  63. <!-- tab切换 -->
  64. <div class="tab-group">
  65. <button
  66. class="tab-btn"
  67. :class="{ active: taber === 'DeepMate' }"
  68. @click="taber = 'DeepMate'"
  69. >
  70. DeepMate
  71. </button>
  72. <button
  73. class="tab-btn"
  74. :class="{ active: taber === 'DeepExplore' }"
  75. @click="taber = 'DeepExplore'"
  76. >
  77. 深度探索
  78. </button>
  79. </div>
  80. <!-- 数据 -->
  81. <el-table
  82. :data="tableDataDM"
  83. style="width: 100%; margin-top: 20px;"
  84. header-cell-class-name="table-header"
  85. @sort-change="handleSortChangeDM"
  86. :default-sort="{ order: null }"
  87. class="table-rounded"
  88. :loading="tableLoadingDM"
  89. >
  90. <el-table-column prop="id" label="序号" align="center" header-align="center" width="60">
  91. <template #default="scope">
  92. {{ (currentPageDM - 1) * pageSizeDM + scope.$index + 1 }}
  93. </template>
  94. </el-table-column>
  95. <el-table-column prop="dccode" label="账号" align="center" header-align="center"/>
  96. <el-table-column prop="dcname" label="姓名" align="center" header-align="center"/>
  97. <el-table-column prop="module_name" label="模块名称" align="center" header-align="center"/>
  98. <el-table-column prop="token_num" label="token数量" align="center" header-align="center"/>
  99. <el-table-column prop="updated_at" label="操作时间" align="center" header-align="center" sortable="custom"/>
  100. <el-table-column label="操作" align="center" header-align="center">
  101. <template #default="scope">
  102. <el-button type="text" @click="handleEditDM(scope.row)">编辑</el-button>
  103. <el-button type="text" @click="handleLogDM(scope.row.dccode)">操作日志</el-button>
  104. </template>
  105. </el-table-column>
  106. </el-table>
  107. <!-- 分页组件 -->
  108. <div class="demo-pagination-block">
  109. <el-pagination
  110. @size-change="handleSizeChangeDM"
  111. @current-change="handleCurrentChangeDM"
  112. :current-page="currentPageDM"
  113. :page-sizes="[10, 20, 50, 100]"
  114. :page-size="pageSizeDM"
  115. layout="total, sizes, prev, pager, next, jumper"
  116. :total= "datatotalDM"
  117. />
  118. </div>
  119. <!-- 开通/编辑弹窗 -->
  120. <el-dialog v-model="dialogVisibleDM" :title="addOrUpdataDM === 1 ? '添加权限' : '修改'" width="500px" :before-close="cancelDM">
  121. <!-- 设置用户 -->
  122. <div class="form-item" v-if="addOrUpdataDM === 1">
  123. <label class="form-label">设置用户</label>
  124. <el-input type="textarea" v-model="hlidsInput" rows=10
  125. placeholder="请输入HLid...
  126. 示例
  127. 90048004
  128. 90048005
  129. 90048006"
  130. />
  131. <div class="tip">支持批量输入每次最多1000个手动/Excel粘贴均可</div>
  132. </div>
  133. <!-- 编辑回显 -->
  134. <div class="info-container" v-if="addOrUpdataDM === 0">
  135. <span class="info-item">HLid: {{ hlidsInput }}</span>
  136. <span class="info-item">剩余次数{{ deadtoken }}</span>
  137. </div>
  138. <!-- 设置数量 -->
  139. <div class="form-item" v-if="addOrUpdataDM === 1">
  140. <label class="form-label">设置用户次数</label>
  141. <div class="count-group">
  142. <!-- Deep Mate不可编辑文本框 -->
  143. <el-input v-model="dmText" disabled style="width: 160px; margin-right: 16px;"/>
  144. <!-- token数量只能正整数 -->
  145. <el-input v-model.number="token_num" type="number" style="width: 210px;" placeholder="请输入次数(只能为正)"/>
  146. </div>
  147. </div>
  148. <div class="form-item" v-if="addOrUpdataDM === 0">
  149. <label class="form-label">修改用户次数</label>
  150. <div class="count-group">
  151. <!-- Deep Mate不可编辑文本框 -->
  152. <el-input v-model="dmText" disabled style="width: 160px; margin-right: 16px;"/>
  153. <!-- token数量支持正负整数 -->
  154. <el-input v-model.number="token_num" type="number" style="width: 210px;" placeholder="请输入次数(可正负)"/>
  155. </div>
  156. </div>
  157. <!-- 按钮区域 -->
  158. <div class="dialog-footer">
  159. <el-button type="default" plain @click="cancelDM">取消</el-button>
  160. <el-button type="primary" @click="submitFormDM" style="background-color: #ff0000; border-color: #ff0000;">提交</el-button>
  161. </div>
  162. </el-dialog>
  163. </div>
  164. </div>
  165. </template>
  166. <script setup>
  167. import { ref, reactive, onMounted } from 'vue';
  168. import { ElMessage } from 'element-plus';
  169. import { marketListApi, userDMListApi, exportDeepMateApi, exitDMApi } from '../../api/userPermissions'
  170. import router from '../../router';
  171. import { useRoute } from 'vue-router';
  172. // token
  173. const token = localStorage.getItem('token')
  174. // tab切换
  175. const taber = ref('DeepMate')
  176. // 获取路由实例
  177. const route = useRoute();
  178. // 组件挂载时:获取地区列表 + 初始化表格数据
  179. onMounted(() => {
  180. const taberQuery = route.query.taber;
  181. const TaberValues = ["DeepMate", "DeepExplore"];
  182. taber.value = TaberValues.includes(String(taberQuery)) ? String(taberQuery) : "DeepMate";
  183. fetchRegionList();
  184. DMTableData();
  185. });
  186. // 地区下拉框
  187. const regionList = ref([]);
  188. const isRegionLoading = ref(false);
  189. // 获取地区列表
  190. const fetchRegionList = async () => {
  191. try {
  192. isRegionLoading.value = true;
  193. const data = await marketListApi({token: token});
  194. regionList.value = data.list;
  195. } catch (error) {
  196. console.error('获取地区列表失败:', error);
  197. regionList.value = [];
  198. } finally {
  199. isRegionLoading.value = false;
  200. }
  201. };
  202. // DeepMate搜索表单
  203. const searchFormDM = reactive({
  204. dccode: '',
  205. dcname: '',
  206. market: '',
  207. user_role: ''
  208. });
  209. // DeepMate排序参数
  210. const sortOrderDM = ref(null);
  211. // DeepMate表格数据
  212. const tableDataDM = ref([]);
  213. const tableLoadingDM = ref(false);
  214. const datatotalDM = ref(0)
  215. // DeepMate分页参数
  216. const currentPageDM = ref(1);
  217. const pageSizeDM = ref(10);
  218. // DeepMate获取表格数据
  219. const DMTableData = async () => {
  220. try {
  221. tableLoadingDM.value = true;
  222. const requestParams = {
  223. token: token,
  224. dccode: searchFormDM.dccode,
  225. dcname: searchFormDM.dcname,
  226. market: searchFormDM.market,
  227. user_role: searchFormDM.user_role,
  228. sort_order: sortOrderDM.value,
  229. page: currentPageDM.value,
  230. page_size: pageSizeDM.value
  231. };
  232. const data = await userDMListApi(requestParams);
  233. tableDataDM.value = data.list
  234. datatotalDM.value = data.total
  235. } catch (error) {
  236. console.error('获取表格数据失败:', error);
  237. tableDataDM.value = [];
  238. datatotalDM.value = 0
  239. } finally {
  240. tableLoadingDM.value = false;
  241. }
  242. };
  243. // DeepMate分页方法
  244. const handleSizeChangeDM = (val) => {
  245. pageSizeDM.value = val;
  246. DMTableData();
  247. console.log(`每页 ${val}`);
  248. };
  249. const handleCurrentChangeDM = (val) => {
  250. currentPageDM.value = val;
  251. DMTableData();
  252. console.log(`当前页: ${val}`);
  253. };
  254. // DeepMate排序事件
  255. const handleSortChangeDM = (sort) => {
  256. const { order } = sort;
  257. sortOrderDM.value = order; // 保存当前排序方式
  258. DMTableData();
  259. };
  260. // DeepMate搜索按钮
  261. const searchDM = () => {
  262. currentPageDM.value = 1;
  263. DMTableData();
  264. };
  265. // DeepMate重置按钮
  266. const resetBnDM = () => {
  267. searchFormDM.dccode = '';
  268. searchFormDM.dcname = '';
  269. searchFormDM.market = '';
  270. searchFormDM.user_role = '';
  271. sortOrderDM.value = null;
  272. currentPageDM.value = 1;
  273. pageSizeDM.value = 10;
  274. DMTableData();
  275. };
  276. // DeepMate开通权限按钮
  277. const enableAccessDM = () => {
  278. dialogVisibleDM.value = true;
  279. addOrUpdataDM.value = 1;
  280. };
  281. // DeepMate导出Excel列表按钮
  282. const exportExcelDM = async () => {
  283. const requestParams = {
  284. token: token,
  285. dccode: searchFormDM.dccode,
  286. dcname: searchFormDM.dcname,
  287. market: searchFormDM.market,
  288. user_role: searchFormDM.user_role,
  289. sort_order: sortOrderDM.value
  290. };
  291. const data = await exportDeepMateApi(requestParams);
  292. if (data != '') {
  293. ElMessage.success('已导出');
  294. }
  295. };
  296. // DeepMate查看导出列表按钮
  297. const exportListDM = () => {
  298. router.push({
  299. path: "/userPermissions/export"
  300. });
  301. };
  302. // 编辑按钮
  303. const handleEditDM = (row) => {
  304. dialogVisibleDM.value = true;
  305. hlidsInput.value = row.dccode;
  306. deadtoken.value = row.token_num;
  307. };
  308. // 操作日志按钮
  309. const handleLogDM = (dccode) => {
  310. router.push({
  311. path: "/userPermissions/logDeepMate",
  312. query: { dccode: dccode }
  313. });
  314. };
  315. // DeepMate弹框显隐控制
  316. const dialogVisibleDM = ref(false);
  317. // DeepMate开通权限表单
  318. const hlidsInput = ref('');
  319. const dmText = ref('Deep Mate');
  320. const token_num = ref('');
  321. // DeepMate编辑回显内容
  322. const deadtoken = ref('');
  323. // DeepMate判断开通还是编辑
  324. const addOrUpdataDM = ref(0);
  325. // 校验HLid
  326. const checkHlids = () => {
  327. // 非空
  328. if (!hlidsInput.value.trim()) {
  329. ElMessage.error('请输入HLid');
  330. return false;
  331. }
  332. // 处理输入:去空、去重,转数组
  333. const hlidList = hlidsInput.value.split('\n')
  334. .map(item => item.trim())
  335. .filter(item => item)
  336. .filter((item, index, self) => self.indexOf(item) === index); // 去重
  337. // 数量校验(最多1000个)
  338. if (hlidList.length > 1000) {
  339. ElMessage.error('HLid数量不能超过1000个');
  340. return false;
  341. }
  342. // 格式校验(8位数字)
  343. const hlidReg = /^\d{8}$/;
  344. for (const hlid of hlidList) {
  345. if (!hlidReg.test(hlid)) {
  346. ElMessage.error(`有HLid格式错误:${hlid},请重新输入`);
  347. return false;
  348. }
  349. }
  350. return true;
  351. };
  352. // 校验token数量
  353. const checkTokenNum = () => {
  354. // 添加权限场景
  355. if (addOrUpdataDM.value === 1) {
  356. if (token_num.value === null || token_num.value === '') {
  357. ElMessage.error('请输入token数量');
  358. return false;
  359. }
  360. if (!Number.isInteger(token_num.value) || token_num.value <= 0) {
  361. ElMessage.error('token数量必须为正整数');
  362. return false;
  363. }
  364. }
  365. // 修改场景
  366. else if (addOrUpdataDM.value === 0) {
  367. if (token_num.value === null || token_num.value === '') {
  368. ElMessage.error('请输入token数量');
  369. return false;
  370. }
  371. if (!Number.isInteger(token_num.value)) {
  372. ElMessage.error('token数量必须为整数');
  373. return false;
  374. }
  375. if (token_num.value < 0 && Math.abs(token_num.value) > deadtoken.value) {
  376. ElMessage.error('扣除次数大于当前总次数,请重新输入次数');
  377. return false;
  378. }
  379. }
  380. return true;
  381. };
  382. // 提交表单
  383. const submitFormDM = async () => {
  384. // 表单校验
  385. if (!checkHlids() || !checkTokenNum()) {
  386. return;
  387. }
  388. try {
  389. const requestParams = {
  390. token: token,
  391. // HLid
  392. hlids: hlidsInput.value.split('\n')
  393. .map(item => item.trim())
  394. .filter(item => item)
  395. .join('\n'),
  396. // token数量
  397. token_num: token_num.value
  398. };
  399. console.log('传给后端的参数:', requestParams);
  400. // 调用后端接口
  401. await exitDMApi(requestParams);
  402. ElMessage.success(addOrUpdataDM.value === 1 ? '用户次数设置成功' : '用户次数修改成功');
  403. // 重置表单并关闭弹框
  404. resetFormDM();
  405. dialogVisibleDM.value = false;
  406. addOrUpdataDM.value = 0;
  407. // 重新获取表格
  408. DMTableData();
  409. } catch (error) {
  410. ElMessage.error('添加权限失败,请重试');
  411. }
  412. };
  413. // DeepMate取消表单
  414. const cancelDM = () => {
  415. resetFormDM();
  416. dialogVisibleDM.value = false;
  417. addOrUpdataDM.value = 0;
  418. };
  419. // DeepMate重置表单数据
  420. const resetFormDM = () => {
  421. hlidsInput.value = '';
  422. token_num.value = '';
  423. };
  424. </script>
  425. <style scoped>
  426. /* 父容器 */
  427. .page-container {
  428. position: relative;
  429. min-height: 600px;
  430. }
  431. /* 搜索区域 */
  432. .search-container {
  433. display: flex;
  434. height: 67px;
  435. flex-direction: column;
  436. justify-content: center;
  437. align-items: flex-start;
  438. gap: 10px;
  439. align-self: stretch;
  440. border-radius: 8px;
  441. background: #FEFAF9;
  442. box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
  443. padding: 0 15px;
  444. margin-bottom: 20px;
  445. }
  446. /* 搜索表单 */
  447. .search-form {
  448. display: flex;
  449. align-items: center;
  450. width: 100%;
  451. gap: 15px;
  452. flex-wrap: nowrap;
  453. }
  454. /* 单个搜索项 */
  455. .search-item {
  456. display: flex;
  457. align-items: center;
  458. gap: 6px;
  459. }
  460. /* 搜索标签文字 */
  461. .form-label {
  462. font-weight: 800 !important;
  463. font-size: 15px;
  464. text-align: left;
  465. color: #333;
  466. margin-top: 13px;
  467. }
  468. /* 按钮组 */
  469. .button-group {
  470. display: flex;
  471. align-items: center;
  472. gap: 0px !important;
  473. margin-left: auto;
  474. }
  475. /* 按钮样式 */
  476. .button-group .el-button {
  477. padding: 6px 10px !important;
  478. font-size: 14px !important;
  479. height: 36px !important;
  480. }
  481. /* 表格样式 */
  482. .table-rounded {
  483. border-radius: 12px !important;
  484. overflow: hidden !important;
  485. border: 1px solid #e4e7ed !important;
  486. }
  487. .table-header {
  488. text-align: center !important;
  489. font-weight: 800 !important;
  490. font-size: 15px !important;
  491. color: #333 !important;
  492. background-color: #f8f9fa !important;
  493. }
  494. .el-table__cell {
  495. border-right: none !important;
  496. border-bottom: 1px solid #e4e7ed !important;
  497. }
  498. .el-table__header th.el-table__cell {
  499. border-right: none !important;
  500. border-bottom: 1px solid #e4e7ed !important;
  501. }
  502. .el-table__row:hover .el-table__cell {
  503. background-color: #fafafa !important;
  504. }
  505. /* 分页组件样式 */
  506. .demo-pagination-block {
  507. display: flex;
  508. width: 100%;
  509. height: 44px;
  510. padding: 0 16px;
  511. align-items: center;
  512. gap: 16px;
  513. position: absolute;
  514. margin-top: 10px;
  515. border-radius: 0 0 3px 3px;
  516. border-top: 1px solid #EAEAEA;
  517. background: #FEFBFB;
  518. box-sizing: border-box;
  519. }
  520. /* 添加/修改样式 */
  521. .form-item {
  522. margin-bottom: 24px;
  523. padding-left: 20px;
  524. padding-right: 20px;
  525. text-align: left;
  526. }
  527. .form-label {
  528. display: block;
  529. margin-bottom: 8px;
  530. font-weight: 500;
  531. }
  532. .radio-group {
  533. display: flex;
  534. flex-direction: column;
  535. gap: 12px;
  536. align-items: flex-start;
  537. }
  538. .radio-item {
  539. display: flex;
  540. align-items: center;
  541. }
  542. .radio-item span {
  543. margin-right: 8px;
  544. }
  545. .tip {
  546. font-size: 12px;
  547. color: #909399;
  548. margin-top: 4px;
  549. }
  550. .dialog-footer {
  551. display: flex;
  552. justify-content: flex-end;
  553. gap: 16px;
  554. margin-top: 20px;
  555. }
  556. .inline-form-item {
  557. display: flex;
  558. align-items: flex-start;
  559. gap: 12px;
  560. }
  561. .inline-form-item .form-label {
  562. display: inline-block;
  563. margin-bottom: 0;
  564. width: 60px;
  565. text-align: left;
  566. padding-right: 12px;
  567. }
  568. .info-container {
  569. display: flex;
  570. align-items: center;
  571. gap: 40px;
  572. padding: 0 20px 18px;
  573. color: #333;
  574. font-size: 16px;
  575. }
  576. .info-item {
  577. white-space: nowrap;
  578. }
  579. /* Tab容器 */
  580. .tab-group {
  581. display: flex;
  582. gap: 12px;
  583. justify-content: flex-start;
  584. margin: 4px 0;
  585. padding-left: 2px;
  586. }
  587. /* Tab按钮 */
  588. .tab-btn {
  589. width: 120px;
  590. padding: 6px 0;
  591. text-align: center;
  592. border: 1px solid #ff0000;
  593. border-radius: 4px;
  594. background-color: #ffffff;
  595. color: #ff0000;
  596. font-size: 14px;
  597. cursor: pointer;
  598. transition: all 0.2s;
  599. outline: none;
  600. }
  601. /* Tab选中样式 */
  602. .tab-btn.active {
  603. background-color: #ff0000;
  604. color: #ffffff;
  605. }
  606. /* hover效果优化 */
  607. .tab-btn:not(.active):hover {
  608. background-color: #fff5f5;
  609. }
  610. </style>