ReviewList.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. <template>
  2. <Breadcrumb pageTitle="审核列表"></Breadcrumb>
  3. <div class="space"></div>
  4. <el-card v-loading="loading">
  5. <el-header>
  6. <div class="action-bar-container">
  7. <el-space class="action-bar-item-container action-bar-action-left">
  8. <el-button-group class="ml-4">
  9. <el-button type="primary" :icon="Refresh" @click="resetFilterAndRefreshTableList">刷新</el-button>
  10. </el-button-group>
  11. </el-space>
  12. <el-space class="action-bar-item-container action-bar-action-right">
  13. <div class="action-bar-item" v-if="showModifyUserFilter">
  14. <span class="action-bar-name">维护人:</span>
  15. <el-select v-model="filters.modifyuser" style="width: 110px" @change="onModifyUserSelectChange" :disabled="modifyUserSelectDisabled">
  16. <el-option
  17. v-for="item in filterConfig.modifyUserList"
  18. :key="item.value"
  19. :label="item.label"
  20. :value="item.value"
  21. />
  22. </el-select>
  23. </div>
  24. <div class="action-bar-item">
  25. <span class="action-bar-name">爬虫状态:</span>
  26. <el-select v-model="filters.state" placeholder="爬虫状态" style="width: 110px" @change="onStateSelectChange">
  27. <el-option
  28. v-for="item in filterConfig.stateOptions"
  29. :key="item.value"
  30. :label="item.label"
  31. :value="item.value"
  32. />
  33. </el-select>
  34. </div>
  35. <!-- <div class="action-bar-item">
  36. <span class="action-bar-name">认领状态:</span>
  37. <el-select v-model="filters.claimtype" placeholder="认领状态" style="width: 110px" @change="onClaimSelectChange">
  38. <el-option
  39. v-for="item in filterConfig.claimOptions"
  40. :key="item.value"
  41. :label="item.label"
  42. :value="item.value"
  43. :disabled="item.disabled"
  44. />
  45. </el-select>
  46. </div> -->
  47. <div class="action-bar-item">
  48. <el-input v-model="filters.search" placeholder="按照爬虫代码搜索" @keydown.enter="onInputSearch" />
  49. </div>
  50. <div class="action-bar-item">
  51. <el-button type="primary" :icon="Search" @click="onInputSearch">搜索</el-button>
  52. </div>
  53. </el-space>
  54. </div>
  55. </el-header>
  56. <el-main>
  57. <el-table ref="spiderTable" :data="listState.list" stripe :row-style="getRowStyle" v-loading="listState.loading">
  58. <el-table-column prop="code" label="代码" show-overflow-tooltip></el-table-column>
  59. <el-table-column prop="site" label="网站" show-overflow-tooltip></el-table-column>
  60. <el-table-column prop="channel" label="栏目" show-overflow-tooltip></el-table-column>
  61. <el-table-column prop="stateText" label="爬虫状态" show-overflow-tooltip></el-table-column>
  62. <el-table-column prop="claimText" label="认领状态" show-overflow-tooltip></el-table-column>
  63. <el-table-column prop="href" label="栏目地址" show-overflow-tooltip></el-table-column>
  64. <el-table-column prop="modifyuser" label="维护人" width="80" show-overflow-tooltip></el-table-column>
  65. <el-table-column label="操作" width="160" align="center">
  66. <template #default="scope">
  67. <!-- <el-tooltip content="标注" placement="top">
  68. <el-button size="small" :class="{ active: scope.row._action_clicked_mark }" @click="tableEvents.handleDataTag(scope.row)">
  69. <el-icon><Link /></el-icon>
  70. </el-button>
  71. </el-tooltip>-->
  72. <el-tooltip content="编辑" placement="top">
  73. <el-button size="small" :class="{ active: scope.row._action_clicked_edit }" @click="tableEvents.handleEdit(scope.$index, scope.row)">
  74. <el-icon><Edit /></el-icon>
  75. </el-button>
  76. </el-tooltip>
  77. <el-tooltip content="调试" placement="top">
  78. <el-button size="small" :class="{ active: scope.row._action_clicked_debug }" @click="tableEvents.handleDebug(scope.$index, scope.row)">
  79. <el-icon><SetUp /></el-icon>
  80. </el-button>
  81. </el-tooltip>
  82. <el-tooltip content="验证" placement="top">
  83. <el-button size="small" :class="{ active: scope.row._action_clicked_verify }" @click="tableEvents.handleVerify(scope.$index, scope.row)">
  84. <el-icon><Aim /></el-icon>
  85. </el-button>
  86. </el-tooltip>
  87. </template>
  88. </el-table-column>
  89. <el-table-column label="功能" width="120" align="center">
  90. <template #default="scope">
  91. <el-tooltip content="提交" placement="top" v-if="showListDevelopeButton">
  92. <!-- 爬虫状态是待完成,才可点击提交 -->
  93. <el-button size="small" :disabled="scope.row.state !== 0" :class="{ active: scope.row._action_clicked_submit }" @click="tableEvents.handleSubmit(scope.$index, scope.row)">
  94. <el-icon><Promotion /></el-icon>
  95. </el-button>
  96. </el-tooltip>
  97. <el-tooltip content="回退" placement="top" v-if="showListRollbackButton">
  98. <!-- 爬虫状态是待完成,才可点击回退 -->
  99. <el-button size="small" :disabled="scope.row.state !== 0" :class="{ active: scope.row._action_clicked_rollback }" @click="tableEvents.handleRollback(scope.$index, scope.row)">
  100. <el-icon><DArrowLeft /></el-icon>
  101. </el-button>
  102. </el-tooltip>
  103. </template>
  104. </el-table-column>
  105. </el-table>
  106. <div class="space"></div>
  107. <div class="pagination-container">
  108. <el-pagination align="right" @size-change="handleSizeChange" @current-change="handleCurrentChange"
  109. :current-page="listState.pageNum" :page-sizes="[10, 20, 30, 40]" :page-size="listState.pageSize"
  110. layout="total, sizes, prev, pager, next, jumper" :total="listState.total">
  111. </el-pagination>
  112. </div>
  113. </el-main>
  114. </el-card>
  115. <EditSpider ref="editSpiderDialog" @custom-event="dialogEvents.editSpiderConfigSaveEvent" @data-tag="editDialogMarkClick($event)" />
  116. <RunSpiderDialog ref="runSpiderDialog" />
  117. <VerifySpider ref="verifySpiderDialog" />
  118. </template>
  119. <script setup>
  120. import { ref, computed, onMounted, onUnmounted, reactive, watch } from 'vue'
  121. import { useRouter } from 'vue-router';
  122. import { useStore } from 'vuex';
  123. import { ElMessage, ElMessageBox } from 'element-plus'
  124. import { BrowserOpenURL, EventsOn } from "../../wailsjs/runtime"
  125. import { VerifySpiderConfig, ServerActionUpdateCodeState, ServerActionCurrentOpenTab } from "../../wailsjs/go/main/App"
  126. import Breadcrumb from "../components/Breadcrumb.vue"
  127. import EditSpider from "../components/spider/EditSpider.vue"
  128. import RunSpiderDialog from "../components/spider/RunSpiderDialog.vue"
  129. import VerifySpider from "../components/spider/VerifySpider.vue"
  130. import { useReviewListFiltersWithRole } from '../composables/filter-options'
  131. import { USER_ROLE_ADMIN, USER_ROLE_DEVELOPER, USER_ROLE_REVIEWER } from '../data/user'
  132. import { Refresh, Search, Box } from '@element-plus/icons-vue'
  133. const router = useRouter();
  134. const store = useStore();
  135. const spiderTable = ref(null)
  136. const { stateOptions, claimOptions } = useReviewListFiltersWithRole()
  137. const loading = ref(false)
  138. const filterConfig = reactive({
  139. // 爬虫状态备选项
  140. stateOptions: stateOptions,
  141. // 认领状态备选项
  142. claimOptions: claimOptions,
  143. // 维护人备选项
  144. modifyUserList: [
  145. {
  146. label: '全部',
  147. value: '-1',
  148. }
  149. ]
  150. })
  151. // 选择器数据(用来重置)
  152. const defaultFilters = {
  153. // 搜索
  154. search: '',
  155. // 爬虫状态(审核员默认待审核1,管理员默认已通过3)
  156. state: 1,
  157. // 维护人
  158. modifyuser: '-1',
  159. // 认领状态
  160. claimtype: -1,
  161. }
  162. // 选择器数据
  163. const filters = reactive({
  164. // 搜索
  165. search: '',
  166. // 爬虫状态(审核员默认待审核1,管理员默认已通过3)
  167. state: 1,
  168. // 维护人
  169. modifyuser: '-1',
  170. // 认领状态
  171. claimtype: -1,
  172. })
  173. // 列表数据
  174. const listState = reactive({
  175. loaded: false,
  176. loading: false,
  177. pageNum: 1, // 页码
  178. pageSize: 10, // 每页多少条
  179. total: 0, // 返回的总数据条数
  180. list: [ // 数据列表
  181. // {
  182. // code: '爬虫代码',
  183. // site: '网站',
  184. // channel: '栏目',
  185. // href: '栏目地址',
  186. // modifyuser: '维护人',
  187. // _id: 'asfasf',
  188. // },
  189. ],
  190. })
  191. // 列表默认数据(用来重置)
  192. const defaultListState = {
  193. loaded: false,
  194. loading: false,
  195. pageNum: 1,
  196. pageSize: 10,
  197. total: 0,
  198. list: [],
  199. }
  200. // 当前编辑的row的数据
  201. const currentEditRow = ref({})
  202. // 上一个点击的row的数据
  203. const prevClickedRow = ref({})
  204. // 用户身份标识
  205. const userRole = computed(() => store.getters.userRole)
  206. // 是否展示维护人模块(管理员和审核员展示)
  207. const showModifyUserFilter = computed(() => [USER_ROLE_ADMIN, USER_ROLE_REVIEWER].includes(userRole.value))
  208. // 是否展示提交按钮(仅开发人员展示)
  209. const showListDevelopeButton = computed(() => [USER_ROLE_DEVELOPER].includes(userRole.value))
  210. // 是否展示回退按钮(仅开发人员展示)
  211. const showListRollbackButton = computed(() => [USER_ROLE_DEVELOPER].includes(userRole.value))
  212. // 实现待认领不能和维护人同时筛选
  213. // 是否禁用维护人模块(待认领筛选下,禁用维护人筛选模块)
  214. const modifyUserSelectDisabled = computed(() => filters.claimtype === 0)
  215. // 是否禁用待认领筛选(维护人不为全部,则禁用待认领的选中)
  216. const stopWatch = watch(() => filters.modifyuser, (n) => {
  217. const target = filterConfig.claimOptions.find(i => i.value === 0)
  218. if (target) {
  219. if (n === defaultFilters.modifyuser) {
  220. // 解除禁用待认领
  221. target.disabled = false
  222. } else {
  223. // 禁用待认领
  224. target.disabled = true
  225. }
  226. } else {
  227. stopWatch()
  228. }
  229. })
  230. const changeFilterDefaultValue = () => {
  231. // 根据用户身份
  232. // 爬虫状态(审核员默认待审核1,管理员默认已通过3)
  233. if ([USER_ROLE_ADMIN].includes(userRole.value)) {
  234. // 管理员
  235. filters.state = 3
  236. defaultFilters.state = 3
  237. }
  238. }
  239. changeFilterDefaultValue()
  240. const editSpiderDialog = ref(null)
  241. const runSpiderDialog = ref(null)
  242. const verifySpiderDialog = ref(null)
  243. // 取消prevClicked的高亮
  244. const cancelOtherHighlight = () => {
  245. for (const key in prevClickedRow.value) {
  246. if (key.includes('_action_clicked')) {
  247. prevClickedRow.value[key] = false
  248. }
  249. }
  250. }
  251. // 页面按钮点击高亮
  252. const onlyClickHighlight = (row, key) => {
  253. cancelOtherHighlight()
  254. row[key] = true
  255. prevClickedRow.value = row
  256. }
  257. const getRowStyle = ({ row }) => {
  258. return row.selected ? { backgroundColor: '#F7F7F7' } : {};
  259. };
  260. // 计算爬虫状态
  261. const calcStateText = (state) => {
  262. const target = filterConfig.stateOptions.find(r => r.value === state)
  263. if (target) {
  264. return target.label
  265. } else {
  266. return ''
  267. }
  268. }
  269. // 计算认领状态
  270. const calcClaimText = (state) => {
  271. const target = filterConfig.claimOptions.find(r => r.value === state)
  272. if (target) {
  273. return target.label
  274. } else {
  275. return ''
  276. }
  277. }
  278. // 获取列表数据
  279. async function getTableList() {
  280. listState.loading = true
  281. try {
  282. const r = await store.dispatch('rulesList/getCodeList', {
  283. modifyuser: filters.modifyuser, // 维护人
  284. state: filters.state, // 爬虫状态
  285. search: filters.search, // 搜索内容
  286. claimtype: filters.claimtype,
  287. pageType: 'reviewlist',
  288. pageSize: listState.pageSize,
  289. pageNum: listState.pageNum
  290. });
  291. const { data, err, msg } = r
  292. if (data) {
  293. listState.total = data.total || 0
  294. if (Array.isArray(data.list)) {
  295. const sList = data.list.map(t => {
  296. return {
  297. ...t,
  298. stateText: calcStateText(t.state),
  299. claimText: calcClaimText(t.claimtype),
  300. // 操作按钮是否点击过
  301. _action_clicked_mark: false,
  302. _action_clicked_edit: false,
  303. _action_clicked_debug: false,
  304. _action_clicked_verify: false,
  305. _action_clicked_submit: false,
  306. _action_clicked_rollback: false,
  307. }
  308. })
  309. listState.list = sList || []
  310. }
  311. }
  312. } catch (error) {
  313. listState.loaded = true
  314. } finally {
  315. listState.loading = false
  316. }
  317. }
  318. // 重置列表数据
  319. const resetListState = () => {
  320. Object.assign(listState, defaultListState)
  321. }
  322. // 重置选择器数据
  323. const resetFilterState = () => {
  324. Object.assign(filters, defaultFilters)
  325. }
  326. // 刷新列表(不重置选择器)
  327. function refreshTableList() {
  328. resetListState()
  329. getTableList()
  330. }
  331. // 刷新列表(并重置选择器)
  332. function resetFilterAndRefreshTableList() {
  333. resetFilterState()
  334. refreshTableList()
  335. }
  336. getTableList()
  337. const handleSizeChange = (val) => {
  338. listState.pageSize = val;
  339. listState.pageNum = 1;
  340. getTableList()
  341. };
  342. const handleCurrentChange = (val) => {
  343. listState.pageNum = val;
  344. getTableList()
  345. };
  346. // 获取维护人劣列表
  347. const getModifyUserList = async () => {
  348. const r = await store.dispatch('rulesList/getModifyUserList')
  349. const { data, err, msg } = r
  350. if (data) {
  351. if (Array.isArray(data.list)) {
  352. const arr = filterConfig.modifyUserList
  353. const reqArr = data.list.map(r => {
  354. return {
  355. label: r.s_name,
  356. value: r.s_name,
  357. }
  358. })
  359. filterConfig.modifyUserList = arr.concat(reqArr)
  360. }
  361. }
  362. }
  363. getModifyUserList()
  364. const onModifyUserSelectChange = () => {
  365. refreshTableList()
  366. }
  367. const onStateSelectChange = () => {
  368. refreshTableList()
  369. }
  370. const onInputSearch = () => {
  371. refreshTableList()
  372. }
  373. const onClaimSelectChange = () => {
  374. refreshTableList()
  375. }
  376. // 保存完成后(不刷新优化),同步更新表单内的数据
  377. const refreshRowData = (code, payload) => {
  378. const target = listState.list.find(item => item.code === code)
  379. if (target) {
  380. if (target.cssmark) {
  381. Object.assign(target.cssmark, payload)
  382. } else {
  383. target.cssmark = payload
  384. }
  385. }
  386. }
  387. // 更新编辑弹窗数据
  388. const refreshAndAsyncEditDialog = (key, value) => {
  389. editSpiderDialog.value.refreshPageData(key, value)
  390. }
  391. // 打开编辑弹窗(如果传数据了,则恢复数据)
  392. const openEditDialog = (row) => {
  393. editSpiderDialog.value.dialogVisible = true
  394. if (row) {
  395. editSpiderDialog.value.setPageData({
  396. ...row,
  397. ...row.cssmark,
  398. })
  399. }
  400. }
  401. const dialogEvents = {
  402. editSpiderConfigSaveEvent: async function (data) {
  403. // 整理数据结构
  404. // [{ query: {code: 'code'}, set: {} }, {query:{},set:{}}]
  405. const rowData = data._originData
  406. const payload = data.value
  407. const code = rowData.code
  408. const updateRule = [
  409. {
  410. query: { code: code },
  411. set: {
  412. cssmark: payload,
  413. }
  414. }
  415. ]
  416. const params = {
  417. stype: 'save',
  418. update: updateRule
  419. }
  420. console.log("change data:", data, params)
  421. try {
  422. const r = await store.dispatch('rulesList/editCodeItem', params)
  423. const { msg, err } = r
  424. if (err === 1) {
  425. refreshRowData(code, payload)
  426. ElMessage({
  427. message: msg || '保存成功',
  428. type: 'success',
  429. duration: 3000,
  430. })
  431. // getTableList()
  432. } else {
  433. ElMessage({
  434. message: msg || '保存失败',
  435. type: 'error',
  436. duration: 3000,
  437. })
  438. }
  439. } catch (error) {
  440. ElMessage({
  441. message: '保存失败',
  442. type: 'error',
  443. duration: 3000,
  444. })
  445. }
  446. },
  447. }
  448. const getMarkWithRow = row => {
  449. const baseInfo = {
  450. code: row.code,
  451. site: row.site,
  452. channel: row.channel,
  453. href: row.href,
  454. modifyuser: row.modifyuser,
  455. }
  456. if (row.cssmark) {
  457. return {
  458. ...row.cssmark,
  459. ...baseInfo,
  460. }
  461. } else {
  462. return baseInfo
  463. }
  464. }
  465. const getLuaParams = row => {
  466. const baseInfo = {
  467. code: row.code,
  468. site: row.site,
  469. channel: row.channel,
  470. modifyuser: row.modifyuser,
  471. claimtime: row.claimtime,
  472. recovertime: row.recovertime,
  473. priority: row.priority,
  474. spiderimportant: row.spiderimportant,
  475. modifytime: row.modifytime,
  476. }
  477. return baseInfo
  478. }
  479. const editDialogMarkClick = (row) => {
  480. // 自定义关闭时间
  481. ElMessage({
  482. message: `${row.site} ${row.channel} ${row.href}`,
  483. showClose: true,
  484. duration: 3000,
  485. });
  486. BrowserOpenURL(row.href)
  487. }
  488. // table的按钮事件集合
  489. const tableEvents = {
  490. handleDataTag(row) {
  491. onlyClickHighlight(row, '_action_clicked_mark')
  492. // 自定义关闭时间
  493. editDialogMarkClick(row)
  494. },
  495. handleEdit: (index, row) => {
  496. currentEditRow.value = row
  497. onlyClickHighlight(row, '_action_clicked_edit')
  498. prevClickedRow.value = row
  499. ElMessage({
  500. message: `${row.site} ${row.channel} ${row.href}`,
  501. showClose: true,
  502. duration: 3000,
  503. });
  504. const mark = getMarkWithRow(row)
  505. ServerActionCurrentOpenTab(mark)
  506. openEditDialog(row)
  507. },
  508. handleDebug(index, row) {
  509. onlyClickHighlight(row, '_action_clicked_debug')
  510. runSpiderDialog.value.dialogVisible = true
  511. runSpiderDialog.value.setPageData(row)
  512. // router.push({
  513. // path: '/run'
  514. // });
  515. },
  516. handleVerify(index, row) {
  517. onlyClickHighlight(row, '_action_clicked_verify')
  518. loading.value = true
  519. if (!row.cssmark) {
  520. return ElMessage({
  521. message: '没有找到标注信息',
  522. type: 'error',
  523. duration: 3000,
  524. })
  525. }
  526. const mark = getMarkWithRow(row)
  527. VerifySpiderConfig(mark).then(r => {
  528. if (r.err === 1 && r.ret) {
  529. verifySpiderDialog.value.dialogVisible = true
  530. verifySpiderDialog.value.formData = r.ret
  531. } else {
  532. return ElMessage({
  533. message: r.msg || '验证异常',
  534. type: 'error',
  535. duration: 3000,
  536. })
  537. }
  538. }).finally(() => {
  539. loading.value = false
  540. })
  541. },
  542. handleSubmit(index, row) {
  543. onlyClickHighlight(row, '_action_clicked_submit')
  544. const lua = getLuaParams(row)
  545. lua.state = 1
  546. ServerActionUpdateCodeState({ lua: lua }).then(r => {
  547. if (r.err === 1) {
  548. ElMessage({
  549. message: '提交成功',
  550. type: 'success',
  551. duration: 3000,
  552. })
  553. getTableList()
  554. } else {
  555. return ElMessage({
  556. message: r.msg || '提交失败',
  557. type: 'error',
  558. duration: 3000,
  559. })
  560. }
  561. })
  562. },
  563. handleRollback(_, row) {
  564. onlyClickHighlight(row, '_action_clicked_rollback')
  565. ElMessageBox.alert('确定回退?', '提示', {
  566. customClass: 'j-confirm-message-box',
  567. confirmButtonText: '确定',
  568. callback: (action) => {
  569. if (action === 'confirm') {
  570. this.confirmRollback(row)
  571. }
  572. },
  573. })
  574. },
  575. confirmRollback(row) {
  576. const lua = getLuaParams(row)
  577. lua.state = 12
  578. ServerActionUpdateCodeState({ lua: lua }).then(r => {
  579. if (r.err === 1) {
  580. ElMessage({
  581. message: '回退成功',
  582. type: 'success',
  583. duration: 3000,
  584. })
  585. getTableList()
  586. } else {
  587. return ElMessage({
  588. message: r.msg || '回退失败',
  589. type: 'error',
  590. duration: 3000,
  591. })
  592. }
  593. })
  594. },
  595. }
  596. //Wails事件绑定
  597. EventsOn("spiderConfigChange", data => {
  598. console.log(data)
  599. const { key, css, url } = data
  600. refreshAndAsyncEditDialog(key, css)
  601. // 当触发修改时候,同步给客服端一份
  602. if (currentEditRow.value && Object.keys(currentEditRow.value).length <= 0) {
  603. const mark = getMarkWithRow(currentEditRow.value)
  604. ServerActionCurrentOpenTab(mark)
  605. }
  606. // 判断标注url和编辑url是否相同
  607. // const editUrl = currentEditRow.href
  608. // if (url === editUrl) {
  609. // refreshAndAsyncEditDialog(key, css[key])
  610. // } else {
  611. // ElMessage({
  612. // message: `标注url和编辑url不匹配,此次更新取消。当前标注url: ${url},当前编辑url: ${editUrl}`,
  613. // type: 'warn',
  614. // duration: 4000,
  615. // })
  616. // }
  617. })
  618. </script>
  619. <style lang="scss" scoped>
  620. ::v-deep {
  621. .el-button {
  622. &.active {
  623. background-color: var(--el-button-hover-bg-color);
  624. border-color: var(--el-button-hover-border-color);
  625. color: var(--el-button-hover-text-color);
  626. outline: none;
  627. }
  628. }
  629. }
  630. .pagination-container {
  631. display: flex;
  632. align-items: center;
  633. justify-content: flex-end;
  634. }
  635. .action-bar-container {
  636. display: flex;
  637. justify-content: space-between;
  638. }
  639. .action-bar-item-container {
  640. }
  641. .action-bar-item {
  642. display: flex;
  643. align-items: center;
  644. }
  645. .action-bar-name {
  646. font-size: 14px;
  647. white-space: nowrap;
  648. }
  649. </style>