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.

1505 lines
39 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 class="page-container" v-show="taber == 'DeepExplore'">
  165. <!-- 搜索区域 -->
  166. <div class="search-containerDE">
  167. <!-- 输入项区域 -->
  168. <div class="search-formDE">
  169. <div class="search-itemDE">
  170. <span class="form-labelDE">账号</span>
  171. <el-input
  172. v-model="searchFormDE.dccode"
  173. placeholder="请输入账号"
  174. clearable
  175. style="height: 36px; width: 140px;"
  176. />
  177. </div>
  178. <div class="search-itemDE">
  179. <span class="form-labelDE">姓名</span>
  180. <el-input
  181. v-model="searchFormDE.dcname"
  182. placeholder="请输入姓名"
  183. clearable
  184. style="height: 36px; width: 180px;"
  185. />
  186. </div>
  187. <div class="search-itemDE">
  188. <span class="form-labelDE">客户类型</span>
  189. <el-select
  190. v-model="searchFormDE.user_role"
  191. placeholder="请选择客户类型"
  192. clearable
  193. style="height: 36px; width: 160px;"
  194. >
  195. <el-option label="非网" value="2" />
  196. <el-option label="会员" value="1" />
  197. </el-select>
  198. </div>
  199. <div class="search-itemDE">
  200. <span class="form-labelDE">地区</span>
  201. <el-select
  202. v-model="searchFormDE.market"
  203. placeholder="请选择地区"
  204. clearable
  205. style="height: 36px; width: 160px;"
  206. :loading="isRegionLoading"
  207. >
  208. <el-option
  209. v-for="region in regionList"
  210. :key="region.id"
  211. :label="region.text_cn"
  212. :value="region.market"
  213. />
  214. </el-select>
  215. </div>
  216. <div class="search-itemDE">
  217. <span class="form-labelDE">指标名称</span>
  218. <el-select
  219. v-model="searchFormDE.indicator_id"
  220. placeholder="请选择指标名称"
  221. clearable
  222. style="height: 36px; width: 160px;"
  223. >
  224. <el-option
  225. v-for="item in indicatorOptions"
  226. :key="item.id"
  227. :label="item.name"
  228. :value="item.id"
  229. />
  230. </el-select>
  231. </div>
  232. </div>
  233. <!-- 按钮组 -->
  234. <div class="button-groupDE">
  235. <el-button type="primary" @click="searchDE">搜索</el-button>
  236. <el-button type="danger" @click="enableAccessDE">开通权限</el-button>
  237. <el-button type="success" @click="exportExcelDE">导出Excel列表</el-button>
  238. <el-button color="#626aef" @click="exportListDE">查看导出列表</el-button>
  239. <el-button type="primary" @click="resetBnDE">重置</el-button>
  240. </div>
  241. </div>
  242. <!-- tab切换 -->
  243. <div class="tab-group">
  244. <button
  245. class="tab-btn"
  246. :class="{ active: taber === 'DeepMate' }"
  247. @click="taber = 'DeepMate'"
  248. >
  249. DeepMate
  250. </button>
  251. <button
  252. class="tab-btn"
  253. :class="{ active: taber === 'DeepExplore' }"
  254. @click="taber = 'DeepExplore'"
  255. >
  256. 深度探索
  257. </button>
  258. </div>
  259. <!-- 数据 -->
  260. <el-table
  261. :data="tableDataDE"
  262. style="width: 100%; margin-top: 20px;"
  263. header-cell-class-name="table-header"
  264. @sort-change="handleSortChangeDE"
  265. :default-sort="{ prop: null, order: null }"
  266. class="table-rounded"
  267. :loading="tableLoadingDE"
  268. >
  269. <el-table-column prop="id" label="序号" align="center" header-align="center" width="60">
  270. <template #default="scope">
  271. {{ (currentPageDE - 1) * pageSizeDE + scope.$index + 1 }}
  272. </template>
  273. </el-table-column>
  274. <el-table-column prop="dccode" label="账号" align="center" header-align="center"/>
  275. <el-table-column prop="dcname" label="姓名" align="center" header-align="center"/>
  276. <el-table-column prop="name" label="指标名称" align="center" header-align="center" width="200">
  277. <template #default="scope">
  278. <el-tooltip
  279. effect="dark"
  280. :content="scope.row.name"
  281. placement="top"
  282. >
  283. <span class="ellipsis-text">{{ scope.row.name }}</span>
  284. </el-tooltip>
  285. </template>
  286. </el-table-column>
  287. <el-table-column prop="created_at" label="开通时间" align="center" header-align="center" sortable="custom" width="200"/>
  288. <el-table-column prop="expire_time" label="到期时间" align="center" header-align="center" sortable="custom" width="200"/>
  289. <el-table-column label="操作" align="center" header-align="center">
  290. <template #default="scope">
  291. <el-button type="text" @click="handleEditDE(scope.row)">编辑</el-button>
  292. <el-button type="text" @click="handleDetailsDE(scope.row.dccode)">权限详情</el-button>
  293. <el-button type="text" @click="handleLogDE(scope.row.dccode)">操作日志</el-button>
  294. </template>
  295. </el-table-column>
  296. </el-table>
  297. <!-- 分页组件 -->
  298. <div class="demo-pagination-block">
  299. <el-pagination
  300. @size-change="handleSizeChangeDE"
  301. @current-change="handleCurrentChangeDE"
  302. :current-page="currentPageDE"
  303. :page-sizes="[10, 20, 50, 100]"
  304. :page-size="pageSizeDE"
  305. layout="total, sizes, prev, pager, next, jumper"
  306. :total= "datatotalDE"
  307. />
  308. </div>
  309. <!-- 开通/编辑 -->
  310. <el-dialog v-model="dialogVisibleDE" :title="addOrUpdataDE === 1 ? '添加权限' : '设置'" width="550px" :before-close="cancelDE">
  311. <!-- 设置用户 -->
  312. <div class="form-item" v-if="addOrUpdataDE === 1">
  313. <label class="form-label">设置用户</label>
  314. <el-input type="textarea" v-model="hlidsInputDE" rows=10
  315. placeholder="请输入HLid...
  316. 示例
  317. 90048004
  318. 90048005
  319. 90048006"
  320. />
  321. <div class="tip">支持批量输入每次最多1000个手动/Excel粘贴均可</div>
  322. </div>
  323. <!-- 编辑回显 -->
  324. <div class="info-container" v-if="addOrUpdataDE === 0">
  325. <span class="info-item">HLid{{ hlidsInputDE }}</span>
  326. <span class="info-item">当前到期时间{{ deadline.split(' ')[0] }}</span>
  327. </div>
  328. <!-- 设置指标 -->
  329. <div class="form-item">
  330. <label class="form-label">选择模板</label>
  331. <el-checkbox-group v-model="indicator_id" class="indicator-checkbox-group">
  332. <el-checkbox
  333. v-for="item in indicatorOptions"
  334. :key="item.id"
  335. :label="item.id"
  336. class="indicator-checkbox-item"
  337. >
  338. {{ item.name }}
  339. </el-checkbox>
  340. </el-checkbox-group>
  341. </div>
  342. <!-- 设置权限时间 -->
  343. <div class="form-item">
  344. <label class="form-label">设置权限时间</label>
  345. <el-radio-group v-model="timeType" class="radio-group">
  346. <el-radio label="expire" class="radio-item">
  347. <span>到期时间</span>
  348. <el-date-picker
  349. v-model="expireTime"
  350. type="date"
  351. placeholder="请选择到期日期"
  352. :disabled-date="disabledDate"
  353. style="width: 220px; margin-left: 8px;"
  354. />
  355. </el-radio>
  356. <el-radio label="delay" class="radio-item">
  357. <span>延期时间</span>
  358. <el-input
  359. v-model.number="delayValue"
  360. type="number"
  361. style="width: 60px; margin: 0 8px;"
  362. placeholder="1"
  363. @input="handleDelayInput"
  364. />
  365. <el-select v-model="delayUnit" placeholder="请选择" style="width: 150px;">
  366. <el-option label="年" value="year"></el-option>
  367. <el-option label="月" value="month"></el-option>
  368. <el-option label="周" value="week"></el-option>
  369. <el-option label="日" value="day"></el-option>
  370. </el-select>
  371. </el-radio>
  372. </el-radio-group>
  373. </div>
  374. <!-- 备注-->
  375. <div class="form-item inline-form-item">
  376. <label class="form-label">备注</label>
  377. <el-input
  378. type="textarea"
  379. v-model="remark"
  380. rows=3
  381. placeholder="请输入备注..."
  382. maxlength="150"
  383. show-word-limit
  384. />
  385. </div>
  386. <!-- 操作人 -->
  387. <div class="form-item inline-form-item">
  388. <label class="form-label">操作人</label>
  389. <el-input v-model="operator" placeholder="请填写操作人"/>
  390. </div>
  391. <!-- 按钮区域 -->
  392. <div class="dialog-footer">
  393. <el-button type="default" plain @click="cancelDE">取消</el-button>
  394. <el-button type="primary" @click="submitFormDE" style="background-color: #ff0000; border-color: #ff0000;">提交</el-button>
  395. </div>
  396. </el-dialog>
  397. <!-- 权限详情 -->
  398. <el-dialog
  399. v-model="showPermissionDetail"
  400. title="权限详情"
  401. width="400px"
  402. :close-on-click-modal="false"
  403. :show-close="true"
  404. class="permission-detail-dialog"
  405. >
  406. <div class="detail-container">
  407. <!-- HLid 信息 -->
  408. <div class="hlid-item">
  409. <span class="label">HLid</span>
  410. <span class="value">{{ HLid }}</span>
  411. </div>
  412. <!-- 到期时间区域 -->
  413. <div class="expire-section">
  414. <h3 class="section-title">到期时间</h3>
  415. <div class="indicator-list">
  416. <div
  417. v-for="(item, index) in permissionData"
  418. :key="index"
  419. class="indicator-item"
  420. >
  421. <span class="indicator-name">{{ item.name }}</span>
  422. <template v-if="item.is_expired === 1">
  423. <span class="expired-tag">已到期</span>
  424. </template>
  425. <template v-else>
  426. <span class="expire-time">{{ item.expire_time.split(' ')[0] }}</span>
  427. </template>
  428. </div>
  429. </div>
  430. </div>
  431. </div>
  432. </el-dialog>
  433. </div>
  434. </div>
  435. </template>
  436. <script setup>
  437. import { ref, reactive, onMounted, computed } from 'vue';
  438. import { ElMessage } from 'element-plus';
  439. import { marketListApi, indicatorListApi, userDMListApi, exportDeepMateApi, exitDMApi, userDEListApi, exportDeepExploreApi, exitDEApi, detailDEApi } from '../../api/userPermissions'
  440. import router from '../../router';
  441. import { useRoute } from 'vue-router';
  442. // token
  443. const token = localStorage.getItem('token')
  444. // tab切换
  445. const taber = ref('DeepMate')
  446. // 获取路由实例
  447. const route = useRoute();
  448. // 组件挂载时:获取地区列表 + 初始化表格数据
  449. onMounted(() => {
  450. const taberQuery = route.query.taber;
  451. const TaberValues = ["DeepMate", "DeepExplore"];
  452. taber.value = TaberValues.includes(String(taberQuery)) ? String(taberQuery) : "DeepMate";
  453. fetchRegionList();
  454. indicatorList();
  455. DMTableData();
  456. DETableData();
  457. });
  458. // 地区下拉框
  459. const regionList = ref([]);
  460. const isRegionLoading = ref(false);
  461. // 获取地区列表
  462. const fetchRegionList = async () => {
  463. try {
  464. isRegionLoading.value = true;
  465. const data = await marketListApi({token: token});
  466. regionList.value = data.list;
  467. } catch (error) {
  468. console.error('获取地区列表失败:', error);
  469. regionList.value = [];
  470. } finally {
  471. isRegionLoading.value = false;
  472. }
  473. };
  474. // DeepMate搜索表单
  475. const searchFormDM = reactive({
  476. dccode: '',
  477. dcname: '',
  478. market: '',
  479. user_role: ''
  480. });
  481. // DeepMate排序参数
  482. const sortOrderDM = ref(null);
  483. // DeepMate表格数据
  484. const tableDataDM = ref([]);
  485. const tableLoadingDM = ref(false);
  486. const datatotalDM = ref(0)
  487. // DeepMate分页参数
  488. const currentPageDM = ref(1);
  489. const pageSizeDM = ref(10);
  490. // DeepMate获取表格数据
  491. const DMTableData = async () => {
  492. try {
  493. tableLoadingDM.value = true;
  494. const requestParams = {
  495. token: token,
  496. dccode: searchFormDM.dccode,
  497. dcname: searchFormDM.dcname,
  498. market: searchFormDM.market,
  499. user_role: searchFormDM.user_role,
  500. sort_order: sortOrderDM.value,
  501. page: currentPageDM.value,
  502. page_size: pageSizeDM.value
  503. };
  504. const data = await userDMListApi(requestParams);
  505. tableDataDM.value = data.list
  506. datatotalDM.value = data.total
  507. } catch (error) {
  508. console.error('获取表格数据失败:', error);
  509. tableDataDM.value = [];
  510. datatotalDM.value = 0
  511. } finally {
  512. tableLoadingDM.value = false;
  513. }
  514. };
  515. // DeepMate分页方法
  516. const handleSizeChangeDM = (val) => {
  517. pageSizeDM.value = val;
  518. DMTableData();
  519. console.log(`每页 ${val}`);
  520. };
  521. const handleCurrentChangeDM = (val) => {
  522. currentPageDM.value = val;
  523. DMTableData();
  524. console.log(`当前页: ${val}`);
  525. };
  526. // DeepMate排序事件
  527. const handleSortChangeDM = (sort) => {
  528. const { order } = sort;
  529. sortOrderDM.value = order; // 保存当前排序方式
  530. DMTableData();
  531. };
  532. // DeepMate搜索按钮
  533. const searchDM = () => {
  534. currentPageDM.value = 1;
  535. DMTableData();
  536. };
  537. // DeepMate重置按钮
  538. const resetBnDM = () => {
  539. searchFormDM.dccode = '';
  540. searchFormDM.dcname = '';
  541. searchFormDM.market = '';
  542. searchFormDM.user_role = '';
  543. sortOrderDM.value = null;
  544. currentPageDM.value = 1;
  545. pageSizeDM.value = 10;
  546. DMTableData();
  547. };
  548. // DeepMate开通权限按钮
  549. const enableAccessDM = () => {
  550. dialogVisibleDM.value = true;
  551. addOrUpdataDM.value = 1;
  552. };
  553. // DeepMate导出Excel列表按钮
  554. const exportExcelDM = async () => {
  555. const requestParams = {
  556. token: token,
  557. dccode: searchFormDM.dccode,
  558. dcname: searchFormDM.dcname,
  559. market: searchFormDM.market,
  560. user_role: searchFormDM.user_role,
  561. sort_order: sortOrderDM.value
  562. };
  563. const data = await exportDeepMateApi(requestParams);
  564. if (data != '') {
  565. ElMessage.success('已导出');
  566. }
  567. };
  568. // DeepMate查看导出列表按钮
  569. const exportListDM = () => {
  570. router.push({
  571. path: "/userPermissions/export"
  572. });
  573. };
  574. // DeepMate编辑按钮
  575. const handleEditDM = (row) => {
  576. dialogVisibleDM.value = true;
  577. hlidsInput.value = row.dccode;
  578. deadtoken.value = row.token_num;
  579. };
  580. // DeepMate操作日志按钮
  581. const handleLogDM = (dccode) => {
  582. router.push({
  583. path: "/userPermissions/logDeepMate",
  584. query: { dccode: dccode }
  585. });
  586. };
  587. // DeepMate弹框显隐控制
  588. const dialogVisibleDM = ref(false);
  589. // DeepMate开通权限表单
  590. const hlidsInput = ref('');
  591. const dmText = ref('Deep Mate');
  592. const token_num = ref('');
  593. // DeepMate编辑回显内容
  594. const deadtoken = ref('');
  595. // DeepMate判断开通还是编辑
  596. const addOrUpdataDM = ref(0);
  597. // 校验HLid
  598. const checkHlids = () => {
  599. // 非空
  600. if (!hlidsInput.value.trim()) {
  601. ElMessage.error('请输入HLid');
  602. return false;
  603. }
  604. // 处理输入:去空、去重,转数组
  605. const hlidList = hlidsInput.value.split('\n')
  606. .map(item => item.trim())
  607. .filter(item => item)
  608. .filter((item, index, self) => self.indexOf(item) === index); // 去重
  609. // 数量校验(最多1000个)
  610. if (hlidList.length > 1000) {
  611. ElMessage.error('HLid数量不能超过1000个');
  612. return false;
  613. }
  614. // 格式校验(8位数字)
  615. const hlidReg = /^\d{8}$/;
  616. for (const hlid of hlidList) {
  617. if (!hlidReg.test(hlid)) {
  618. ElMessage.error(`有HLid格式错误:${hlid},请重新输入`);
  619. return false;
  620. }
  621. }
  622. return true;
  623. };
  624. // 校验token数量
  625. const checkTokenNum = () => {
  626. // 添加权限场景
  627. if (addOrUpdataDM.value === 1) {
  628. if (token_num.value === null || token_num.value === '') {
  629. ElMessage.error('请输入token数量');
  630. return false;
  631. }
  632. if (!Number.isInteger(token_num.value) || token_num.value <= 0) {
  633. ElMessage.error('token数量必须为正整数');
  634. return false;
  635. }
  636. }
  637. // 修改场景
  638. else if (addOrUpdataDM.value === 0) {
  639. if (token_num.value === null || token_num.value === '') {
  640. ElMessage.error('请输入token数量');
  641. return false;
  642. }
  643. if (!Number.isInteger(token_num.value)) {
  644. ElMessage.error('token数量必须为整数');
  645. return false;
  646. }
  647. if (token_num.value < 0 && Math.abs(token_num.value) > deadtoken.value) {
  648. ElMessage.error('扣除次数大于当前总次数,请重新输入次数');
  649. return false;
  650. }
  651. }
  652. return true;
  653. };
  654. // DeepMate提交表单
  655. const submitFormDM = async () => {
  656. // 表单校验
  657. if (!checkHlids() || !checkTokenNum()) {
  658. return;
  659. }
  660. try {
  661. const requestParams = {
  662. token: token,
  663. // HLid
  664. hlids: hlidsInput.value.split('\n')
  665. .map(item => item.trim())
  666. .filter(item => item)
  667. .join('\n'),
  668. // token数量
  669. token_num: token_num.value
  670. };
  671. console.log('传给后端的参数:', requestParams);
  672. // 调用后端接口
  673. await exitDMApi(requestParams);
  674. ElMessage.success(addOrUpdataDM.value === 1 ? '用户次数设置成功' : '用户次数修改成功');
  675. // 重置表单并关闭弹框
  676. resetFormDM();
  677. dialogVisibleDM.value = false;
  678. addOrUpdataDM.value = 0;
  679. // 重新获取表格
  680. DMTableData();
  681. } catch (error) {
  682. ElMessage.error('添加权限失败,请重试');
  683. }
  684. };
  685. // DeepMate取消表单
  686. const cancelDM = () => {
  687. resetFormDM();
  688. dialogVisibleDM.value = false;
  689. addOrUpdataDM.value = 0;
  690. };
  691. // DeepMate重置表单数据
  692. const resetFormDM = () => {
  693. hlidsInput.value = '';
  694. token_num.value = '';
  695. };
  696. // 深度探索指标选项
  697. const indicatorOptions = ref([]);
  698. const indicatorList = async () => {
  699. try {
  700. const data = await indicatorListApi({token: token});
  701. indicatorOptions.value = data.list;
  702. } catch (error) {
  703. console.error('获取指标列表失败:', error);
  704. indicatorOptions.value = [];
  705. }
  706. }
  707. // 深度探索搜索表单
  708. const searchFormDE = reactive({
  709. dccode: '',
  710. dcname: '',
  711. market: '',
  712. user_role: '',
  713. indicator_id: ''
  714. });
  715. // 深度探索排序参数
  716. const sortPropDE = ref(null);
  717. const sortOrderDE = ref(null);
  718. // 深度探索分页参数
  719. const currentPageDE = ref(1);
  720. const pageSizeDE = ref(10);
  721. // 深度探索表格数据
  722. const tableDataDE = ref([]);
  723. const tableLoadingDE = ref(false);
  724. const datatotalDE = ref(0)
  725. // 深度探索分页方法
  726. const handleSizeChangeDE = (val) => {
  727. pageSizeDE.value = val;
  728. DETableData();
  729. console.log(`每页 ${val}`);
  730. };
  731. const handleCurrentChangeDE = (val) => {
  732. currentPageDE.value = val;
  733. DETableData();
  734. console.log(`当前页: ${val}`);
  735. };
  736. // 深度探索排序事件
  737. const handleSortChangeDE = (sort) => {
  738. const { prop, order } = sort;
  739. if (!['created_at', 'expire_time'].includes(prop)) return;
  740. sortPropDE.value = prop; // 保存当前排序字段
  741. sortOrderDE.value = order; // 保存当前排序方式
  742. DETableData();
  743. };
  744. // 深度探索获取表格数据
  745. const DETableData = async () => {
  746. try {
  747. tableLoadingDE.value = true;
  748. const requestParams = {
  749. token: token,
  750. dccode: searchFormDE.dccode,
  751. dcname: searchFormDE.dcname,
  752. market: searchFormDE.market,
  753. user_role: searchFormDE.user_role,
  754. indicator_id: searchFormDE.indicator_id,
  755. sort_field: sortPropDE.value,
  756. sort_order: sortOrderDE.value,
  757. page: currentPageDE.value,
  758. page_size: pageSizeDE.value
  759. };
  760. const data = await userDEListApi(requestParams);
  761. tableDataDE.value = data.list
  762. datatotalDE.value = data.total
  763. } catch (error) {
  764. console.error('获取表格数据失败:', error);
  765. tableDataDE.value = [];
  766. datatotalDE.value = 0
  767. } finally {
  768. tableLoadingDE.value = false;
  769. }
  770. };
  771. // 深度探索搜索按钮
  772. const searchDE = () => {
  773. currentPageDE.value = 1;
  774. DETableData();
  775. };
  776. // 深度探索重置按钮
  777. const resetBnDE = () => {
  778. searchFormDE.dccode = '';
  779. searchFormDE.dcname = '';
  780. searchFormDE.market = '';
  781. searchFormDE.user_role = '';
  782. searchFormDE.indicator_id = '';
  783. sortPropDE.value = null;
  784. sortOrderDE.value = null;
  785. currentPageDE.value = 1;
  786. pageSizeDE.value = 10;
  787. DETableData();
  788. };
  789. // 深度探索导出Excel列表按钮
  790. const exportExcelDE = async () => {
  791. const requestParams = {
  792. token: token,
  793. dccode: searchFormDE.dccode,
  794. dcname: searchFormDE.dcname,
  795. market: searchFormDE.market,
  796. user_role: searchFormDE.user_role,
  797. indicator_id: searchFormDE.indicator_id,
  798. sort_field: sortPropDE.value,
  799. sort_order: sortOrderDE.value
  800. };
  801. const data = await exportDeepExploreApi(requestParams);
  802. if (data != '') {
  803. ElMessage.success('已导出');
  804. }
  805. };
  806. // 深度探索查看导出列表按钮
  807. const exportListDE = () => {
  808. router.push({
  809. path: "/userPermissions/export"
  810. });
  811. };
  812. // 深度探索开通权限按钮
  813. const enableAccessDE = () => {
  814. dialogVisibleDE.value = true;
  815. addOrUpdataDE.value = 1;
  816. };
  817. // 深度探索编辑按钮
  818. const handleEditDE = (row) => {
  819. dialogVisibleDE.value = true;
  820. hlidsInputDE.value = row.dccode;
  821. deadline.value = row.expire_time;
  822. };
  823. // 深度探索权限详情按钮
  824. const handleDetailsDE = (dccode) =>{
  825. openDetail(dccode)
  826. }
  827. // 深度探索操作日志按钮
  828. const handleLogDE = (dccode) => {
  829. router.push({
  830. path: "/userPermissions/logDeepExplore",
  831. query: { dccode: dccode }
  832. });
  833. };
  834. // 弹框显隐控制
  835. const dialogVisibleDE = ref(false);
  836. // 开通权限表单
  837. const hlidsInputDE = ref('');
  838. const indicator_id = ref([]);
  839. const timeType = ref('');
  840. const expireTime = ref('');
  841. const delayValue = ref('');
  842. const delayUnit = ref('');
  843. const remark = ref('');
  844. const operator = ref('');
  845. // 判断开通还是编辑
  846. const addOrUpdataDE = ref(0);
  847. // 编辑回显内容
  848. const deadline = ref('');
  849. // 将数组转为顿号拼接的字符串
  850. const indicatorStr = computed(() => {
  851. return indicator_id.value?.join('、') || '';
  852. });
  853. // 禁用当前日期之前的日期(当天0点之前的时间)
  854. const disabledDate = (time) => {
  855. return time.getTime() < new Date().getTime() - 8.64e7;
  856. };
  857. // 格式化日期
  858. const formatDate = (date) => {
  859. if (!date) return '';
  860. const year = date.getFullYear();
  861. const month = String(date.getMonth() + 1).padStart(2, '0');
  862. const day = String(date.getDate()).padStart(2, '0');
  863. return `${year}/${month}/${day}`;
  864. };
  865. // 禁止为小数
  866. const handleDelayInput = () => {
  867. if (delayValue.value !== null && delayValue.value !== undefined) {
  868. delayValue.value = Math.floor(delayValue.value);
  869. }
  870. };
  871. // 校验HLid
  872. const checkHlidsDE = () => {
  873. // 非空
  874. if (!hlidsInputDE.value.trim()) {
  875. ElMessage.error('请输入HLid');
  876. return false;
  877. }
  878. // 处理输入:去空、去重,转数组
  879. const hlidList = hlidsInputDE.value.split('\n')
  880. .map(item => item.trim())
  881. .filter(item => item)
  882. .filter((item, index, self) => self.indexOf(item) === index); // 去重
  883. // 数量校验(最多1000个)
  884. if (hlidList.length > 1000) {
  885. ElMessage.error('HLid数量不能超过1000个');
  886. return false;
  887. }
  888. // 格式校验(8位数字)
  889. const hlidReg = /^\d{8}$/;
  890. for (const hlid of hlidList) {
  891. if (!hlidReg.test(hlid)) {
  892. ElMessage.error(`有HLid格式错误:${hlid},请重新输入`);
  893. return false;
  894. }
  895. }
  896. return true;
  897. };
  898. // 校验指标
  899. const checkmodel = () => {
  900. if (indicator_id.value.length === 0) {
  901. ElMessage.error('请至少选择一个指标');
  902. return false;
  903. }
  904. return true;
  905. };
  906. // 校验时间
  907. const checkTime = () => {
  908. if (timeType.value === 'expire') {
  909. // 到期时间
  910. if (!expireTime.value) {
  911. ElMessage.error('请选择到期时间');
  912. return false;
  913. }
  914. } else if (timeType.value === 'delay') {
  915. // 延期时间
  916. if (!delayValue.value || !delayUnit.value) {
  917. ElMessage.error('延期时间必须填写完整(数值+单位)');
  918. return false;
  919. }
  920. const delayNum = Number(delayValue.value);
  921. if (isNaN(delayNum) || delayNum < 0) {
  922. ElMessage.error('延期时间不能为负数,请输入有效正数');
  923. return false;
  924. }
  925. if (delayNum === 0) {
  926. ElMessage.error('延期时间不能为0,请输入有效正数');
  927. return false;
  928. }
  929. } else {
  930. ElMessage.error('请设置权限时间');
  931. return false;
  932. }
  933. return true;
  934. };
  935. // 校验备注
  936. const checkRemark = () => {
  937. if (!remark.value.trim()) {
  938. ElMessage.error('请输入备注');
  939. return false;
  940. }
  941. return true;
  942. };
  943. // 提交表单
  944. const submitFormDE = async () => {
  945. // 表单校验
  946. if (!checkHlidsDE() || !checkmodel() || !checkTime() || !checkRemark()) {
  947. return;
  948. }
  949. try {
  950. // 组装后端要求的参数格式
  951. const requestParams = {
  952. token: token,
  953. // HLid
  954. hlids: hlidsInputDE.value.split('\n')
  955. .map(item => item.trim())
  956. .filter(item => item)
  957. .join('\n'),
  958. // 指标
  959. indicator_id: indicatorStr.value,
  960. // 备注
  961. remark: remark.value.trim(),
  962. // 操作人
  963. ...(operator.value.trim() && { operator: operator.value.trim() }),
  964. // 时间参数
  965. ...(timeType.value === 'expire'
  966. ? { expire_time: formatDate(expireTime.value) }
  967. : {
  968. [delayUnit.value]: Number(delayValue.value)
  969. })
  970. };
  971. console.log('传给后端的参数:', requestParams);
  972. // 调用后端接口
  973. await exitDEApi(requestParams);
  974. ElMessage.success(addOrUpdataDM.value === 1 ? '成功添加用户权限' : '成功修改用户权限');
  975. // 重置表单并关闭弹框
  976. resetForm();
  977. dialogVisibleDE.value = false;
  978. addOrUpdataDE.value = 0;
  979. // 重新获取表格
  980. DETableData();
  981. } catch (error) {
  982. ElMessage.error('添加权限失败,请重试');
  983. }
  984. };
  985. // 深度探索重置表单数据
  986. const resetForm = () => {
  987. hlidsInputDE.value = '';
  988. indicator_id.value = [];
  989. timeType.value = '';
  990. expireTime.value = '';
  991. delayValue.value = '';
  992. delayUnit.value = '';
  993. remark.value = '';
  994. operator.value = '';
  995. };
  996. // 深度探索取消表单
  997. const cancelDE = () => {
  998. resetForm();
  999. dialogVisibleDE.value = false;
  1000. addOrUpdataDE.value = 0;
  1001. };
  1002. // 深度探索权限详情弹窗开关
  1003. const showPermissionDetail = ref(false);
  1004. // 深度探索权限详情弹窗数据
  1005. const HLid = ref('');
  1006. const permissionData = ref([]);
  1007. // 深度探索权限详情获取数据并开启弹窗
  1008. const openDetail = async(dccode) => {
  1009. HLid.value = dccode
  1010. // 后端调用
  1011. const res = await detailDEApi({
  1012. token: token,
  1013. dccode: dccode
  1014. })
  1015. permissionData.value = res
  1016. // 显示弹窗
  1017. showPermissionDetail.value = true;
  1018. };
  1019. </script>
  1020. <style scoped>
  1021. /* 父容器 */
  1022. .page-container {
  1023. position: relative;
  1024. min-height: 600px;
  1025. }
  1026. /* 搜索区域 */
  1027. .search-container {
  1028. display: flex;
  1029. height: 67px;
  1030. flex-direction: column;
  1031. justify-content: center;
  1032. align-items: flex-start;
  1033. gap: 10px;
  1034. align-self: stretch;
  1035. border-radius: 8px;
  1036. background: #FEFAF9;
  1037. box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
  1038. padding: 0 15px;
  1039. margin-bottom: 20px;
  1040. }
  1041. /* 搜索表单 */
  1042. .search-form {
  1043. display: flex;
  1044. align-items: center;
  1045. width: 100%;
  1046. gap: 15px;
  1047. flex-wrap: nowrap;
  1048. }
  1049. /* 单个搜索项 */
  1050. .search-item {
  1051. display: flex;
  1052. align-items: center;
  1053. gap: 6px;
  1054. }
  1055. /* 搜索标签文字 */
  1056. .form-label {
  1057. font-weight: 800 !important;
  1058. font-size: 15px;
  1059. text-align: left;
  1060. color: #333;
  1061. margin-top: 13px;
  1062. }
  1063. /* 按钮组 */
  1064. .button-group {
  1065. display: flex;
  1066. align-items: center;
  1067. gap: 0px !important;
  1068. margin-left: auto;
  1069. }
  1070. /* 按钮样式 */
  1071. .button-group .el-button {
  1072. padding: 6px 10px !important;
  1073. font-size: 14px !important;
  1074. height: 36px !important;
  1075. }
  1076. /* 表格样式 */
  1077. .table-rounded {
  1078. border-radius: 12px !important;
  1079. overflow: hidden !important;
  1080. border: 1px solid #e4e7ed !important;
  1081. }
  1082. .table-header {
  1083. text-align: center !important;
  1084. font-weight: 800 !important;
  1085. font-size: 15px !important;
  1086. color: #333 !important;
  1087. background-color: #f8f9fa !important;
  1088. }
  1089. .el-table__cell {
  1090. border-right: none !important;
  1091. border-bottom: 1px solid #e4e7ed !important;
  1092. }
  1093. .el-table__header th.el-table__cell {
  1094. border-right: none !important;
  1095. border-bottom: 1px solid #e4e7ed !important;
  1096. }
  1097. .el-table__row:hover .el-table__cell {
  1098. background-color: #fafafa !important;
  1099. }
  1100. /* 分页组件样式 */
  1101. .demo-pagination-block {
  1102. display: flex;
  1103. width: 100%;
  1104. height: 44px;
  1105. padding: 0 16px;
  1106. align-items: center;
  1107. gap: 16px;
  1108. position: absolute;
  1109. margin-top: 10px;
  1110. border-radius: 0 0 3px 3px;
  1111. border-top: 1px solid #EAEAEA;
  1112. background: #FEFBFB;
  1113. box-sizing: border-box;
  1114. }
  1115. /* 添加/修改样式 */
  1116. .form-item {
  1117. margin-bottom: 24px;
  1118. padding-left: 20px;
  1119. padding-right: 20px;
  1120. text-align: left;
  1121. }
  1122. .form-label {
  1123. display: block;
  1124. margin-bottom: 8px;
  1125. font-weight: 500;
  1126. }
  1127. .radio-group {
  1128. display: flex;
  1129. flex-direction: column;
  1130. gap: 12px;
  1131. align-items: flex-start;
  1132. }
  1133. .radio-item {
  1134. display: flex;
  1135. align-items: center;
  1136. }
  1137. .radio-item span {
  1138. margin-right: 8px;
  1139. }
  1140. .tip {
  1141. font-size: 12px;
  1142. color: #909399;
  1143. margin-top: 4px;
  1144. }
  1145. .dialog-footer {
  1146. display: flex;
  1147. justify-content: flex-end;
  1148. gap: 16px;
  1149. margin-top: 20px;
  1150. }
  1151. .inline-form-item {
  1152. display: flex;
  1153. align-items: flex-start;
  1154. gap: 12px;
  1155. }
  1156. .inline-form-item .form-label {
  1157. display: inline-block;
  1158. margin-bottom: 0;
  1159. width: 60px;
  1160. text-align: left;
  1161. padding-right: 12px;
  1162. }
  1163. .info-container {
  1164. display: flex;
  1165. align-items: center;
  1166. gap: 40px;
  1167. padding: 0 20px 18px;
  1168. color: #333;
  1169. font-size: 16px;
  1170. }
  1171. .info-item {
  1172. white-space: nowrap;
  1173. }
  1174. /* Tab容器 */
  1175. .tab-group {
  1176. display: flex;
  1177. gap: 12px;
  1178. justify-content: flex-start;
  1179. margin: 4px 0;
  1180. padding-left: 2px;
  1181. }
  1182. /* Tab按钮 */
  1183. .tab-btn {
  1184. width: 120px;
  1185. padding: 6px 0;
  1186. text-align: center;
  1187. border: 1px solid #ff0000;
  1188. border-radius: 4px;
  1189. background-color: #ffffff;
  1190. color: #ff0000;
  1191. font-size: 14px;
  1192. cursor: pointer;
  1193. transition: all 0.2s;
  1194. outline: none;
  1195. }
  1196. /* Tab选中样式 */
  1197. .tab-btn.active {
  1198. background-color: #ff0000;
  1199. color: #ffffff;
  1200. }
  1201. /* hover效果优化 */
  1202. .tab-btn:not(.active):hover {
  1203. background-color: #fff5f5;
  1204. }
  1205. /* 搜索区域(深度探索) */
  1206. .search-containerDE {
  1207. display: flex;
  1208. height: auto;
  1209. flex-direction: column;
  1210. justify-content: center;
  1211. align-items: flex-start;
  1212. gap: 12px;
  1213. align-self: stretch;
  1214. border-radius: 8px;
  1215. background: #FEFAF9;
  1216. box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.25);
  1217. padding: 15px;
  1218. margin-bottom: 20px;
  1219. }
  1220. /* 搜索表单(深度探索) */
  1221. .search-formDE {
  1222. display: flex;
  1223. align-items: center;
  1224. width: 100%;
  1225. gap: 15px;
  1226. flex-wrap: wrap;
  1227. row-gap: 8px;
  1228. }
  1229. /* 单个搜索项(深度探索) */
  1230. .search-itemDE {
  1231. display: flex;
  1232. align-items: center;
  1233. gap: 6px;
  1234. }
  1235. /* 搜索标签文字(深度探索) */
  1236. .form-labelDE {
  1237. font-weight: 800 !important;
  1238. font-size: 15px;
  1239. text-align: left;
  1240. color: #333;
  1241. margin-top: 0;
  1242. }
  1243. /* 按钮组(深度探索) */
  1244. .button-groupDE {
  1245. display: flex;
  1246. align-items: center;
  1247. gap: 10px !important;
  1248. }
  1249. /* 按钮样式(深度探索) */
  1250. .button-groupDE .el-button {
  1251. padding: 6px 10px !important;
  1252. font-size: 14px !important;
  1253. height: 36px !important;
  1254. }
  1255. /* 文本溢出省略样式(深度探索) */
  1256. .ellipsis-text {
  1257. display: inline-block;
  1258. width: 100%;
  1259. white-space: nowrap;
  1260. overflow: hidden;
  1261. text-overflow: ellipsis;
  1262. vertical-align: middle;
  1263. }
  1264. /* 开通/编辑指标复选(深度探索) */
  1265. .indicator-checkbox-group {
  1266. display: flex;
  1267. flex-wrap: wrap;
  1268. gap: 8px;
  1269. margin-top: 8px;
  1270. width: 100%
  1271. }
  1272. .indicator-checkbox-item {
  1273. flex: 0 0 calc(20% - 5px);
  1274. box-sizing: border-box;
  1275. padding: 4px 0;
  1276. }
  1277. :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
  1278. background-color: #ff0000 !important;
  1279. border-color: #ff0000 !important;
  1280. }
  1281. :deep(.el-checkbox__input.is-checked .el-checkbox__inner::after) {
  1282. border-color: #fff !important;
  1283. }
  1284. :deep(.el-checkbox__input:hover .el-checkbox__inner) {
  1285. border-color: #ff0000 !important;
  1286. }
  1287. :deep(.el-checkbox__input:focus .el-checkbox__inner) {
  1288. box-shadow: 0 0 0 2px rgba(255, 0, 0, 0.2) !important;
  1289. }
  1290. :deep(.el-checkbox__label) {
  1291. color: #333 !important;
  1292. font-size: 14px !important;
  1293. }
  1294. /* 权限详情(深度探索) */
  1295. .permission-detail-dialog {
  1296. --el-dialog-padding-primary: 15px;
  1297. }
  1298. .detail-container {
  1299. background-color: #fff1f0;
  1300. border-radius: 6px;
  1301. padding: 15px;
  1302. }
  1303. .hlid-item {
  1304. margin-bottom: 25px;
  1305. }
  1306. .label {
  1307. color: #666;
  1308. font-weight: 500;
  1309. }
  1310. .value {
  1311. color: #333;
  1312. }
  1313. .expire-section {
  1314. margin-top: 10px;
  1315. }
  1316. .section-title {
  1317. font-size: 16px;
  1318. color: #333;
  1319. margin-bottom: 10px;
  1320. font-weight: bold;
  1321. }
  1322. .indicator-list {
  1323. display: flex;
  1324. flex-direction: column;
  1325. gap: 8px;
  1326. }
  1327. .indicator-item {
  1328. padding: 6px 0;
  1329. display: flex;
  1330. align-items: center;
  1331. }
  1332. .indicator-name {
  1333. color: #666;
  1334. font-size: 14px;
  1335. }
  1336. .expire-time {
  1337. color: #333;
  1338. font-size: 14px;
  1339. margin-left: 8px;
  1340. }
  1341. .expired-tag {
  1342. background-color: #ffccd5;
  1343. color: #f56c6c;
  1344. font-size: 12px;
  1345. padding: 2px 8px;
  1346. border-radius: 4px;
  1347. margin-left: 8px;
  1348. }
  1349. </style>