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.

1569 lines
41 KiB

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