ReviewList.vue 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  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 type="primary" :icon="Box" @click="doBatchRollback" v-if="showBatchListing">批量打回</el-button>
  11. <el-button type="primary" :icon="Box" @click="doBatchListing" v-if="showBatchListing">批量上线</el-button>
  12. <el-button type="primary" :icon="Box" @click="doUnBatchListing" v-if="showBatchListing">批量下架</el-button>
  13. </el-button-group>
  14. </el-space>
  15. <el-space class="action-bar-item-container action-bar-action-right">
  16. <div class="action-bar-item" v-if="showModifyUserFilter">
  17. <span class="action-bar-name">维护人:</span>
  18. <el-select v-model="filters.modifyuser" style="width: 110px" @change="onModifyUserSelectChange" :disabled="modifyUserSelectDisabled">
  19. <el-option
  20. v-for="item in filterConfig.modifyUserList"
  21. :key="item.value"
  22. :label="item.label"
  23. :value="item.value"
  24. />
  25. </el-select>
  26. </div>
  27. <div class="action-bar-item">
  28. <span class="action-bar-name">爬虫状态:</span>
  29. <el-select v-model="filters.state" placeholder="爬虫状态" style="width: 110px" @change="onStateSelectChange">
  30. <el-option
  31. v-for="item in filterConfig.stateOptions"
  32. :key="item.value"
  33. :label="item.label"
  34. :value="item.value"
  35. />
  36. </el-select>
  37. </div>
  38. <!-- <div class="action-bar-item">
  39. <span class="action-bar-name">认领状态:</span>
  40. <el-select v-model="filters.claimtype" placeholder="认领状态" style="width: 110px" @change="onClaimSelectChange">
  41. <el-option
  42. v-for="item in filterConfig.claimOptions"
  43. :key="item.value"
  44. :label="item.label"
  45. :value="item.value"
  46. :disabled="item.disabled"
  47. />
  48. </el-select>
  49. </div> -->
  50. <div class="action-bar-item">
  51. <el-input v-model="filters.search" placeholder="按照站点、爬虫代码搜索" @keydown.enter="onInputSearch" />
  52. </div>
  53. <div class="action-bar-item">
  54. <el-button type="primary" :icon="Search" @click="onInputSearch">搜索</el-button>
  55. </div>
  56. </el-space>
  57. </div>
  58. </el-header>
  59. <el-main>
  60. <el-table ref="spiderTable" :data="listState.list" stripe :row-style="getRowStyle" @selection-change="handleSelectionChange" v-loading="listState.loading">
  61. <el-table-column type="selection" width="55" :selectable="tableRowSelectable" v-if="isAdmin" />
  62. <el-table-column prop="site" label="网站" align="left" show-overflow-tooltip></el-table-column>
  63. <el-table-column prop="channel" label="栏目" align="left" show-overflow-tooltip></el-table-column>
  64. <el-table-column prop="code" label="代码" align="left" show-overflow-tooltip></el-table-column>
  65. <el-table-column prop="stateText" label="爬虫状态" width="80" align="center" show-overflow-tooltip></el-table-column>
  66. <el-table-column prop="claimText" label="认领状态" width="100" align="center" show-overflow-tooltip></el-table-column>
  67. <el-table-column prop="href" label="栏目地址" show-overflow-tooltip></el-table-column>
  68. <el-table-column prop="modifyuser" label="维护人" width="80" align="center" show-overflow-tooltip></el-table-column>
  69. <el-table-column label="操作" width="160" align="center">
  70. <template #default="scope">
  71. <!-- <el-tooltip content="标注" placement="top">
  72. <el-button size="small" :class="{ active: scope.row._action_clicked_mark }" @click="tableEvents.handleDataTag(scope.row)">
  73. <el-icon><Link /></el-icon>
  74. </el-button>
  75. </el-tooltip>-->
  76. <el-tooltip content="编辑" placement="top">
  77. <el-button size="small" :class="{ active: scope.row._action_clicked_edit }" @click="tableEvents.handleEdit(scope.$index, scope.row)">
  78. <el-icon><Edit /></el-icon>
  79. </el-button>
  80. </el-tooltip>
  81. <el-tooltip content="调试" placement="top">
  82. <el-button size="small" :class="{ active: scope.row._action_clicked_debug }" @click="tableEvents.handleDebug(scope.$index, scope.row)">
  83. <el-icon><SetUp /></el-icon>
  84. </el-button>
  85. </el-tooltip>
  86. <el-tooltip content="验证" placement="top">
  87. <el-button size="small" :class="{ active: scope.row._action_clicked_verify }" @click="tableEvents.handleVerify(scope.$index, scope.row)">
  88. <el-icon><Aim /></el-icon>
  89. </el-button>
  90. </el-tooltip>
  91. </template>
  92. </el-table-column>
  93. <el-table-column label="功能" width="160" align="center">
  94. <template #default="scope">
  95. <!-- 管理员:上线和退回 -->
  96. <template v-if="tableActionShow.adminGroup(scope.row)">
  97. <el-tooltip content="下架" placement="top" v-if="tableActionShow.downCode(scope.row)">
  98. <el-button size="small" :class="{ active: scope.row._action_clicked_down_code }" :disabled="actionButtonDisabled.adminDownCode(scope.row)" @click="tableEvents.adminDownCode(scope.$index, scope.row)">
  99. <el-icon><CircleCloseFilled /></el-icon>
  100. </el-button>
  101. </el-tooltip>
  102. <el-tooltip content="打回" placement="top" v-if="tableActionShow.adminReject(scope.row)">
  103. <el-button size="small" :class="{ active: scope.row._action_clicked_reject_code }" :disabled="actionButtonDisabled.adminReject(scope.row)" @click="tableEvents.adminReject(scope.$index, scope.row)">
  104. <el-icon><CircleClose /></el-icon>
  105. </el-button>
  106. </el-tooltip>
  107. <el-tooltip content="上线" placement="top" v-if="tableActionShow.adminSubmit(scope.row)">
  108. <el-button size="small" :class="{ active: scope.row._action_clicked_submit }" :disabled="actionButtonDisabled.adminSubmit(scope.row)" @click="tableEvents.adminSubmit(scope.$index, scope.row)">
  109. <el-icon><UploadFilled /></el-icon>
  110. </el-button>
  111. </el-tooltip>
  112. <el-tooltip content="退回" placement="top" v-if="tableActionShow.adminRollback(scope.row)">
  113. <el-button size="small" :class="{ active: scope.row._action_clicked_rollback }" @click="tableEvents.adminRollback(scope.$index, scope.row)">
  114. <el-icon><DArrowLeft /></el-icon>
  115. </el-button>
  116. </el-tooltip>
  117. </template>
  118. <!-- 审核人员:通过和打回,管理员也有此功能 -->
  119. <el-tooltip content="通过" placement="top" v-if="tableActionShow.reviewerGroup(scope.row)">
  120. <el-button size="small" :class="{ active: scope.row._action_clicked_submit }" :disabled="actionButtonDisabled.reviewerSubmit(scope.row)" @click="tableEvents.reviewerSubmit(scope.$index, scope.row)">
  121. <el-icon><CircleCheck /></el-icon>
  122. </el-button>
  123. </el-tooltip>
  124. <el-tooltip content="打回" placement="top" v-if="!actionButtonDisabled.reviewerRollback(scope.row)">
  125. <el-button size="small" :class="{ active: scope.row._action_clicked_rollback }" @click="tableEvents.reviewerRollback(scope.$index, scope.row)">
  126. <el-icon><CircleClose /></el-icon>
  127. </el-button>
  128. </el-tooltip>
  129. <el-tooltip content="退回" placement="top" v-if="tableActionShow.reviewerRollback2(scope.row)">
  130. <el-button size="small" :class="{ active: scope.row._action_clicked_reject_code }" @click="tableEvents.adminRollback(scope.$index, scope.row)">
  131. <el-icon><DArrowLeft /></el-icon>
  132. </el-button>
  133. </el-tooltip>
  134. </template>
  135. </el-table-column>
  136. </el-table>
  137. <div class="space"></div>
  138. <div class="pagination-container">
  139. <el-pagination align="right" @size-change="handleSizeChange" @current-change="handleCurrentChange"
  140. :current-page="listState.pageNum" :page-sizes="[10, 20, 30, 40]" :page-size="listState.pageSize"
  141. layout="total, sizes, prev, pager, next, jumper" :total="listState.total">
  142. </el-pagination>
  143. </div>
  144. </el-main>
  145. </el-card>
  146. <EditSpider
  147. ref="editSpiderDialog"
  148. from="reviewList"
  149. @custom-event="dialogEvents.editSpiderConfigSaveEvent"
  150. @data-tag="editDialogMarkClick($event)"
  151. />
  152. <RunSpiderDialog from="reviewList" ref="runSpiderDialog" />
  153. <VerifySpider ref="verifySpiderDialog" />
  154. <el-dialog v-model="dialog.rollbackReason" title="选择退回原因" width="500">
  155. <el-form>
  156. <el-form-item label="退回原因">
  157. <el-select v-model="filters.rollbackReason" placeholder="请选择">
  158. <el-option v-for="item in filterConfig.rollbackOptions" :key="item.value" :label="item.label" :value="item.value" />
  159. </el-select>
  160. </el-form-item>
  161. </el-form>
  162. <template #footer>
  163. <div class="dialog-footer">
  164. <el-button @click="dialog.rollbackReason = false">取消</el-button>
  165. <el-button type="primary" @click="tableEvents.confirmRollbackReason(currentEditRow)">
  166. 确定
  167. </el-button>
  168. </div>
  169. </template>
  170. </el-dialog>
  171. </template>
  172. <script setup>
  173. import { ref, computed, onMounted, onUnmounted, reactive, watch } from 'vue'
  174. import { useRouter } from 'vue-router';
  175. import { useStore } from 'vuex';
  176. import { ElMessage, ElMessageBox } from 'element-plus'
  177. import { BrowserOpenURL, EventsOn } from "../../wailsjs/runtime"
  178. import { VerifySpiderConfig, ServerActionUpdateCodeState, ServerActionCurrentOpenTab } from "../../wailsjs/go/main/App"
  179. import Breadcrumb from "../components/Breadcrumb.vue"
  180. import EditSpider from "../components/spider/EditSpider.vue"
  181. import RunSpiderDialog from "../components/spider/RunSpiderDialog.vue"
  182. import VerifySpider from "../components/spider/VerifySpider.vue"
  183. import { useReviewListFiltersWithRole } from '../composables/filter-options'
  184. import { USER_ROLE_ADMIN, USER_ROLE_DEVELOPER, USER_ROLE_REVIEWER } from '../data/user'
  185. import { Refresh, Search, Box } from '@element-plus/icons-vue'
  186. import { rollbackReasonList } from "../data/index.js";
  187. const router = useRouter();
  188. const store = useStore();
  189. const spiderTable = ref(null)
  190. const { stateOptions, claimOptions } = useReviewListFiltersWithRole()
  191. const loading = ref(false)
  192. const filterConfig = reactive({
  193. // 爬虫状态备选项
  194. stateOptions: stateOptions,
  195. // 认领状态备选项
  196. claimOptions: claimOptions,
  197. // 维护人备选项
  198. modifyUserList: [
  199. {
  200. label: '全部',
  201. value: '-1',
  202. }
  203. ],
  204. rollbackOptions: rollbackReasonList
  205. })
  206. // 选择器数据(用来重置)
  207. const defaultFilters = {
  208. // 搜索
  209. search: '',
  210. // 爬虫状态(审核员默认待审核1,管理员默认已通过3)
  211. state: 1,
  212. // 维护人
  213. modifyuser: '-1',
  214. // 认领状态
  215. claimtype: -1,
  216. rollbackReason: undefined,
  217. }
  218. // 选择器数据
  219. const filters = reactive({
  220. // 搜索
  221. search: '',
  222. // 爬虫状态(审核员默认待审核1,管理员默认已通过3)
  223. state: 1,
  224. // 维护人
  225. modifyuser: '-1',
  226. // 认领状态
  227. claimtype: -1,
  228. rollbackReason: undefined,
  229. })
  230. // 列表数据
  231. const listState = reactive({
  232. loaded: false,
  233. loading: false,
  234. pageNum: 1, // 页码
  235. pageSize: 10, // 每页多少条
  236. total: 0, // 返回的总数据条数
  237. list: [ // 数据列表
  238. // {
  239. // code: '爬虫代码',
  240. // site: '网站',
  241. // channel: '栏目',
  242. // href: '栏目地址',
  243. // modifyuser: '维护人',
  244. // _id: 'asfasf',
  245. // },
  246. ],
  247. // 已选中列表
  248. selected: [],
  249. })
  250. // 列表默认数据(用来重置)
  251. const defaultListState = {
  252. loaded: false,
  253. loading: false,
  254. pageNum: 1,
  255. pageSize: 10,
  256. total: 0,
  257. list: [],
  258. }
  259. const dialog = reactive({
  260. rollbackReason: false
  261. })
  262. // 当前编辑的row的数据
  263. const currentEditRow = ref({})
  264. // 上一个点击的row的数据
  265. const prevClickedRow = ref({})
  266. // 用户身份标识
  267. const userRole = computed(() => store.getters.userRole)
  268. // 是否展示维护人模块(管理员和审核员展示)
  269. const showModifyUserFilter = computed(() => [USER_ROLE_ADMIN, USER_ROLE_REVIEWER].includes(userRole.value))
  270. // 实现待认领不能和维护人同时筛选
  271. // 是否禁用维护人模块(待认领筛选下,禁用维护人筛选模块)
  272. const modifyUserSelectDisabled = computed(() => filters.claimtype === 0)
  273. // 是否禁用待认领筛选(维护人不为全部,则禁用待认领的选中)
  274. // const stopWatch = watch(() => filters.modifyuser, (n) => {
  275. // const target = filterConfig.claimOptions.find(i => i.value === 0)
  276. // if (target) {
  277. // if (n === defaultFilters.modifyuser) {
  278. // // 解除禁用待认领
  279. // target.disabled = false
  280. // } else {
  281. // // 禁用待认领
  282. // target.disabled = true
  283. // }
  284. // } else {
  285. // stopWatch()
  286. // }
  287. // })
  288. const isReviewer = computed(() => [USER_ROLE_REVIEWER].includes(userRole.value))
  289. const isAdmin = computed(() => [USER_ROLE_ADMIN].includes(userRole.value))
  290. const showBatchListing = computed(() => isAdmin.value)
  291. const changeFilterDefaultValue = () => {
  292. // 根据用户身份
  293. // 爬虫状态(审核员默认待审核1,管理员默认已通过3)
  294. if ([USER_ROLE_ADMIN].includes(userRole.value)) {
  295. // 管理员
  296. filters.state = 3
  297. defaultFilters.state = 3
  298. }
  299. }
  300. changeFilterDefaultValue()
  301. const editSpiderDialog = ref(null)
  302. const runSpiderDialog = ref(null)
  303. const verifySpiderDialog = ref(null)
  304. // 取消prevClicked的高亮
  305. const cancelOtherHighlight = () => {
  306. for (const key in prevClickedRow.value) {
  307. if (key.includes('_action_clicked')) {
  308. prevClickedRow.value[key] = false
  309. }
  310. }
  311. }
  312. // 页面按钮点击高亮
  313. const onlyClickHighlight = (row, key) => {
  314. cancelOtherHighlight()
  315. row[key] = true
  316. prevClickedRow.value = row
  317. }
  318. const getRowStyle = ({ row }) => {
  319. return row.selected ? { backgroundColor: '#F7F7F7' } : {};
  320. };
  321. const handleSelectionChange = (r) => {
  322. listState.selected = r
  323. }
  324. // 计算爬虫状态
  325. const calcStateText = (state) => {
  326. const target = filterConfig.stateOptions.find(r => r.value === state)
  327. if (target) {
  328. return target.label
  329. } else {
  330. return ''
  331. }
  332. }
  333. // 计算认领状态
  334. const calcClaimText = (state) => {
  335. const target = filterConfig.claimOptions.find(r => r.value === state)
  336. if (target) {
  337. return target.label
  338. } else {
  339. return ''
  340. }
  341. }
  342. // 获取列表数据
  343. async function getTableList() {
  344. listState.loading = true
  345. try {
  346. const r = await store.dispatch('rulesList/getCodeList', {
  347. modifyuser: filters.modifyuser, // 维护人
  348. state: filters.state, // 爬虫状态
  349. search: filters.search, // 搜索内容
  350. claimtype: filters.claimtype,
  351. pageType: 'reviewlist',
  352. pageSize: listState.pageSize,
  353. pageNum: listState.pageNum
  354. });
  355. const { data, err, msg } = r
  356. if (data) {
  357. listState.total = data.total || 0
  358. if (Array.isArray(data.list)) {
  359. const sList = data.list.map(t => {
  360. return {
  361. ...t,
  362. stateText: calcStateText(t.state),
  363. claimText: calcClaimText(t.claimtype),
  364. // 操作按钮是否点击过
  365. _action_clicked_mark: false,
  366. _action_clicked_edit: false,
  367. _action_clicked_debug: false,
  368. _action_clicked_verify: false,
  369. _action_clicked_down_code: false,
  370. _action_clicked_reject_code: false,
  371. _action_clicked_submit: false,
  372. _action_clicked_rollback: false,
  373. }
  374. })
  375. listState.list = sList || []
  376. }
  377. }
  378. } catch (error) {
  379. listState.loaded = true
  380. } finally {
  381. listState.loading = false
  382. }
  383. }
  384. // 重置列表数据
  385. const resetListState = () => {
  386. Object.assign(listState, defaultListState)
  387. }
  388. // 重置选择器数据
  389. const resetFilterState = () => {
  390. Object.assign(filters, defaultFilters)
  391. }
  392. // 刷新列表(不重置选择器)
  393. function refreshTableList() {
  394. resetListState()
  395. getTableList()
  396. }
  397. // 刷新列表(并重置选择器)
  398. function resetFilterAndRefreshTableList() {
  399. resetFilterState()
  400. refreshTableList()
  401. }
  402. getTableList()
  403. const handleSizeChange = (val) => {
  404. listState.pageSize = val;
  405. listState.pageNum = 1;
  406. getTableList()
  407. };
  408. const handleCurrentChange = (val) => {
  409. listState.pageNum = val;
  410. getTableList()
  411. };
  412. // 获取维护人列表
  413. const getModifyUserList = async () => {
  414. const r = await store.dispatch('rulesList/getModifyUserList')
  415. const { data, err, msg } = r
  416. if (data) {
  417. if (Array.isArray(data.list)) {
  418. const arr = filterConfig.modifyUserList
  419. const reqArr = data.list.map(r => {
  420. return {
  421. label: r.s_name,
  422. value: r.s_name,
  423. }
  424. })
  425. filterConfig.modifyUserList = arr.concat(reqArr)
  426. }
  427. }
  428. }
  429. getModifyUserList()
  430. const onModifyUserSelectChange = () => {
  431. refreshTableList()
  432. }
  433. const onStateSelectChange = () => {
  434. refreshTableList()
  435. }
  436. const onInputSearch = () => {
  437. refreshTableList()
  438. }
  439. const onClaimSelectChange = () => {
  440. refreshTableList()
  441. }
  442. // 保存完成后(不刷新优化),同步更新表单内的数据
  443. const refreshRowData = (code, payload) => {
  444. const target = listState.list.find(item => item.code === code)
  445. if (target) {
  446. if (target.cssmark) {
  447. Object.assign(target.cssmark, payload)
  448. } else {
  449. target.cssmark = payload
  450. }
  451. }
  452. }
  453. // 更新编辑弹窗数据
  454. const refreshAndAsyncEditDialog = (key, value) => {
  455. editSpiderDialog.value.refreshPageData(key, value)
  456. }
  457. // 打开编辑弹窗(如果传数据了,则恢复数据)
  458. const openEditDialog = (row) => {
  459. editSpiderDialog.value.dialogVisible = true
  460. if (row) {
  461. editSpiderDialog.value.setPageData({
  462. ...row,
  463. ...row.cssmark,
  464. })
  465. }
  466. }
  467. // 管理员身份 待审核和未通过。通过打回按钮显示不能点
  468. // 管理员身份 已通过,只有上线,其他不展示
  469. // 管理员 已上线。只有退回按钮
  470. // 操作按钮组的显示隐藏控制
  471. const tableActionShow = {
  472. reviewerGroup(row) {
  473. // 已通过、已下架和已上线的不展示
  474. const canOnline = row.state === 3 // 已通过
  475. const alreadyOffline = row.state === 6 // 已下架
  476. const alreadyOnline = row.state === 11 // 已上线
  477. const pass = canOnline || alreadyOffline || alreadyOnline
  478. return !pass
  479. },
  480. adminGroup(row) {
  481. if (isAdmin.value) {
  482. return true
  483. } else {
  484. return false
  485. }
  486. },
  487. adminSubmit(row) {
  488. // 只有审核通过或者下架的才能上线,否则不展示
  489. const canOnline = row.state === 3 || row.state === 6
  490. return canOnline
  491. },
  492. downCode(row) {
  493. // 下架按钮,只有已上线爬虫展示
  494. return row.state === 11
  495. },
  496. adminReject(row) {
  497. // 下架按钮,只有已上线爬虫展示
  498. return row.state === 11
  499. },
  500. adminRollback(row) {
  501. // 只有已上线,才展示退回
  502. return row.state === 11
  503. },
  504. reviewerRollback2(row) {
  505. return isReviewer.value
  506. }
  507. }
  508. const actionButtonDisabled = {
  509. reviewerSubmit(row) {
  510. // 只有待审核的才能点击
  511. let canClick = false
  512. if (isAdmin.value) {
  513. canClick = row.state === 1 || row.state === 3
  514. } else {
  515. canClick = row.state === 1
  516. }
  517. return !canClick
  518. },
  519. reviewerRollback(row) {
  520. return this.reviewerSubmit(row)
  521. },
  522. reviewerRollback2(row) {
  523. return this.reviewerSubmit(row)
  524. },
  525. adminSubmit(row) {
  526. // 只有审核通过才能上线,否则不展示
  527. const canOnline = row.state === 3 || row.state === 6
  528. return !canOnline
  529. },
  530. adminDownCode(row) {
  531. return row.state !== 11
  532. },
  533. adminReject(row) {
  534. return row.state !== 11
  535. }
  536. }
  537. const dialogEvents = {
  538. editSpiderConfigSaveEvent: async function (data) {
  539. // 整理数据结构
  540. // [{ query: {code: 'code'}, set: {} }, {query:{},set:{}}]
  541. const rowData = data._originData
  542. const payload = data.value
  543. const code = rowData.code
  544. const updateRule = [
  545. {
  546. query: { code: code },
  547. set: {
  548. cssmark: payload,
  549. }
  550. }
  551. ]
  552. const params = {
  553. stype: 'save',
  554. update: updateRule
  555. }
  556. try {
  557. const r = await store.dispatch('rulesList/editCodeItem', params)
  558. const { msg, err } = r
  559. if (err === 1) {
  560. refreshRowData(code, payload)
  561. ElMessage({
  562. message: msg || '保存成功',
  563. type: 'success',
  564. duration: 3000,
  565. })
  566. // getTableList()
  567. } else {
  568. ElMessage({
  569. message: msg || '保存失败',
  570. type: 'error',
  571. duration: 3000,
  572. })
  573. }
  574. } catch (error) {
  575. ElMessage({
  576. message: '保存失败',
  577. type: 'error',
  578. duration: 3000,
  579. })
  580. }
  581. },
  582. }
  583. const getMarkWithRow = row => {
  584. const baseInfo = {
  585. code: row.code,
  586. site: row.site,
  587. channel: row.channel,
  588. href: row.href,
  589. modifyuser: row.modifyuser,
  590. }
  591. if (row.cssmark) {
  592. return {
  593. ...row.cssmark,
  594. ...baseInfo,
  595. }
  596. } else {
  597. return baseInfo
  598. }
  599. }
  600. const getLuaParams = row => {
  601. const baseInfo = {
  602. code: row.code,
  603. site: row.site,
  604. channel: row.channel,
  605. modifyuser: row.modifyuser,
  606. claimtime: row.claimtime,
  607. recovertime: row.recovertime,
  608. priority: row.priority,
  609. spiderimportant: row.spiderimportant,
  610. modifytime: row.modifytime,
  611. }
  612. return baseInfo
  613. }
  614. const editDialogMarkClick = (row) => {
  615. // 自定义关闭时间
  616. ElMessage({
  617. message: `${row.site} ${row.channel} ${row.href}`,
  618. showClose: true,
  619. duration: 3000,
  620. });
  621. BrowserOpenURL(row.href)
  622. }
  623. // table的按钮事件集合
  624. const tableEvents = {
  625. handleDataTag(row) {
  626. onlyClickHighlight(row, '_action_clicked_mark')
  627. // 自定义关闭时间
  628. editDialogMarkClick(row)
  629. },
  630. handleEdit: (index, row) => {
  631. currentEditRow.value = row
  632. onlyClickHighlight(row, '_action_clicked_edit')
  633. prevClickedRow.value = row
  634. ElMessage({
  635. message: `${row.site} ${row.channel} ${row.href}`,
  636. showClose: true,
  637. duration: 3000,
  638. });
  639. const mark = getMarkWithRow(row)
  640. ServerActionCurrentOpenTab(mark)
  641. openEditDialog(row)
  642. },
  643. handleDebug(index, row) {
  644. onlyClickHighlight(row, '_action_clicked_debug')
  645. runSpiderDialog.value.dialogVisible = true
  646. runSpiderDialog.value.setPageData(row)
  647. // router.push({
  648. // path: '/run'
  649. // });
  650. },
  651. handleVerify(index, row) {
  652. onlyClickHighlight(row, '_action_clicked_verify')
  653. loading.value = true
  654. if (!row.cssmark) {
  655. loading.value = false
  656. return ElMessage({
  657. message: '没有找到标注信息',
  658. type: 'error',
  659. duration: 3000,
  660. })
  661. }
  662. const mark = getMarkWithRow(row)
  663. VerifySpiderConfig(mark).then(r => {
  664. if (r.err === 1 && r.ret) {
  665. verifySpiderDialog.value.setPageData(true, {
  666. ret: r.ret,
  667. row
  668. })
  669. } else {
  670. return ElMessage({
  671. message: r.msg || '验证异常',
  672. type: 'error',
  673. duration: 3000,
  674. })
  675. }
  676. }).finally(() => {
  677. loading.value = false
  678. })
  679. },
  680. // 操作按钮-----
  681. updateCodeStateAction(rowArr, info) {
  682. const luaArr = []
  683. if (Array.isArray(rowArr) && rowArr.length > 0) {
  684. rowArr.forEach(row => {
  685. const lua = getLuaParams(row)
  686. lua.state = info.state
  687. lua.reason = info.reason
  688. luaArr.push(lua)
  689. })
  690. } else {
  691. return console.error('至少选中1个操作内容')
  692. }
  693. if (!info.stype) {
  694. return console.error('没有找到关键参数stype')
  695. }
  696. const param = {
  697. stype: info.stype,
  698. }
  699. ServerActionUpdateCodeState({ lua: luaArr, param }).then(r => {
  700. if (r.err === 1) {
  701. ElMessage({
  702. message: r.msg || info.successTip,
  703. type: 'success',
  704. duration: 3000,
  705. })
  706. getTableList()
  707. } else {
  708. return ElMessage({
  709. message: r.msg || info.errorTip,
  710. type: 'error',
  711. duration: 3000,
  712. })
  713. }
  714. })
  715. },
  716. // 上线和批量上线操作 stateText=up
  717. // 下架和批量下架操作 stateText=down
  718. batchListing(list = [], stateText = 'up') {
  719. const stateMap = {
  720. up: {
  721. code: 11,
  722. text: '上线',
  723. },
  724. down: {
  725. code: 6,
  726. text: '下架',
  727. },
  728. }
  729. const stateInfo = stateMap[stateText]
  730. if (!stateInfo) {
  731. return console.error('未定义的stateText')
  732. }
  733. let info = {
  734. successTip: `${stateInfo.text}成功`,
  735. errorTip: `${stateInfo.text}失败`,
  736. }
  737. if (!Array.isArray(list)) return
  738. if (list.length <= 0) {
  739. return console.error('至少选中一个可操作对象')
  740. }
  741. if (list.length > 1) {
  742. info = {
  743. successTip: `批量${stateInfo.text}成功`,
  744. errorTip: `批量${stateInfo.text}失败`,
  745. }
  746. }
  747. const luaArr = list.map(r => {
  748. const lua = getLuaParams(r)
  749. lua.state = r.state
  750. return lua
  751. })
  752. const param = {
  753. stype: stateInfo.text
  754. }
  755. ServerActionUpdateCodeState({ lua: luaArr, param }).then(r => {
  756. if (r.err === 1) {
  757. ElMessage({
  758. message: r.msg || info.successTip,
  759. type: 'success',
  760. duration: 3000,
  761. })
  762. getTableList()
  763. } else {
  764. return ElMessage({
  765. message: r.msg || info.errorTip,
  766. type: 'error',
  767. duration: 3000,
  768. })
  769. }
  770. })
  771. },
  772. // 审核打回
  773. reviewerRollback(_, row) {
  774. onlyClickHighlight(row, '_action_clicked_rollback')
  775. ElMessageBox.prompt('请输入打回原因', '审核打回', {
  776. confirmButtonText: '确认打回',
  777. cancelButtonText: '取消',
  778. inputPattern: /^.+$/, // 非空
  779. inputErrorMessage: '不能为空',
  780. })
  781. .then(({ value }) => {
  782. this.updateCodeStateAction([row], {
  783. reason: value,
  784. stype: '打回',
  785. state: 2,
  786. successTip: '打回成功',
  787. errorTip: '打回失败',
  788. })
  789. })
  790. },
  791. // 审核通过
  792. reviewerSubmit(_, row) {
  793. onlyClickHighlight(row, '_action_clicked_submit')
  794. ElMessageBox.confirm('确认通过?', '提示',
  795. {
  796. customClass: 'j-confirm-message-box',
  797. type: 'warning',
  798. confirmButtonText: '确认',
  799. cancelButtonText: '取消',
  800. showCancelButton: false,
  801. }
  802. ).then(() => {
  803. this.updateCodeStateAction([row], {
  804. state: 3,
  805. stype: '通过',
  806. successTip: '通过成功',
  807. errorTip: '通过失败',
  808. })
  809. })
  810. },
  811. // 管理上线
  812. adminSubmit(_, row) {
  813. onlyClickHighlight(row, '_action_clicked_submit')
  814. ElMessageBox.confirm('确认上线?', '提示',
  815. {
  816. customClass: 'j-confirm-message-box',
  817. type: 'warning',
  818. confirmButtonText: '确认',
  819. cancelButtonText: '取消',
  820. showCancelButton: false,
  821. }
  822. ).then(() => {
  823. this.batchListing([row], 'up')
  824. })
  825. },
  826. // 管理控制下架
  827. adminDownCode(_, row) {
  828. onlyClickHighlight(row, '_action_clicked_down_code')
  829. ElMessageBox.confirm('确认下架?', '提示',
  830. {
  831. customClass: 'j-confirm-message-box',
  832. type: 'warning',
  833. confirmButtonText: '确认',
  834. cancelButtonText: '取消',
  835. showCancelButton: false,
  836. }
  837. ).then(() => {
  838. this.batchListing([row], 'down')
  839. })
  840. },
  841. // 管理退回
  842. adminRollback(_, row) {
  843. onlyClickHighlight(row, '_action_clicked_rollback')
  844. currentEditRow.value = row
  845. // ElMessageBox.confirm('确认退回?', '提示',
  846. // {
  847. // customClass: 'j-confirm-message-box',
  848. // type: 'warning',
  849. // confirmButtonText: '确认',
  850. // cancelButtonText: '取消',
  851. // showCancelButton: false,
  852. // }
  853. // ).then(() => {
  854. // tableEvents.confirmAdminRollback(row)
  855. // })
  856. dialog.rollbackReason = true
  857. },
  858. confirmRollbackReason(row) {
  859. if (!filters.rollbackReason) {
  860. return ElMessage({
  861. message: '请选择退回原因',
  862. type: 'error',
  863. duration: 3000,
  864. })
  865. }
  866. dialog.rollbackReason = false
  867. tableEvents.confirmAdminRollback(row)
  868. },
  869. confirmAdminRollback: function (row) {
  870. this.updateCodeStateAction([row], {
  871. state: 12,
  872. stype: '退回',
  873. reason: filters.rollbackReason,
  874. successTip: '退回成功',
  875. errorTip: '退回失败',
  876. })
  877. },
  878. // 管理员打回
  879. adminReject(_, row) {
  880. onlyClickHighlight(row, '_action_clicked_reject_code')
  881. ElMessageBox.prompt('请输入打回原因', '打回', {
  882. confirmButtonText: '确认打回',
  883. cancelButtonText: '取消',
  884. inputPattern: /^.+$/, // 非空
  885. inputErrorMessage: '不能为空',
  886. })
  887. .then(({ value }) => {
  888. this.updateCodeStateAction([row], {
  889. reason: value,
  890. stype: '打回',
  891. state: 2,
  892. successTip: '打回成功',
  893. errorTip: '打回失败',
  894. })
  895. })
  896. }
  897. }
  898. const toggleRow = (rowList = []) => {
  899. const ignoreSelectable = false
  900. rowList.forEach(row => {
  901. spiderTable.value?.toggleRowSelection(
  902. row,
  903. undefined,
  904. ignoreSelectable
  905. )
  906. })
  907. }
  908. const tableRowSelectable = (row) => {
  909. // 待审核(管理员的批量打回)、已通过(管理员的批量上线)、已下架的批量上线或者已上线(管理员的批量下架)的才能选
  910. return row.state === 1 || row.state === 3 || row.state === 6 || row.state === 11
  911. }
  912. // 批量打回
  913. const confirmBatchRollback = () => {
  914. // 过滤一下,找到已上线爬虫,仅提交此类爬虫
  915. let list = []
  916. let dropList = []
  917. if (isAdmin.value) {
  918. // 管理员可以打回待审核和已通过爬虫
  919. list = listState.selected.filter(v => v.state === 1 || v.state === 3 || v.state === 11)
  920. dropList = listState.selected.filter(v => !(v.state === 1 || v.state === 3 || v.state === 11))
  921. } else {
  922. // 审核员只能打回待审核爬虫
  923. list = listState.selected.filter(v => v.state === 1)
  924. dropList = listState.selected.filter(v => v.state !== 1)
  925. }
  926. toggleRow(dropList)
  927. if (list.length <= 0) {
  928. return ElMessage({
  929. message: '至少要选中1条有效数据: 当前未选中待审核或者已通过爬虫',
  930. type: 'error',
  931. duration: 3000,
  932. })
  933. }
  934. tableEvents.updateCodeStateAction(list, {
  935. stype: '打回',
  936. state: 2,
  937. successTip: '批量打回成功',
  938. errorTip: '批量打回失败',
  939. })
  940. }
  941. const doBatchRollback = () => {
  942. ElMessageBox.confirm('确认批量打回?', '提示',
  943. {
  944. customClass: 'j-confirm-message-box',
  945. type: 'warning',
  946. confirmButtonText: '确认',
  947. cancelButtonText: '取消',
  948. showCancelButton: false,
  949. }
  950. ).then(() => {
  951. confirmBatchRollback()
  952. })
  953. }
  954. // 执行批量上线
  955. const confirmBatchListing = () => {
  956. // 过滤一下,找到已通过爬虫,仅提交此类爬虫
  957. const list = listState.selected.filter(v => v.state === 3 || v.state === 6)
  958. const dropList = listState.selected.filter(v => !(v.state === 3 || v.state === 6))
  959. toggleRow(dropList)
  960. if (list.length <= 0) {
  961. return ElMessage({
  962. message: '至少要选中1条有效数据: 当前未选中已通过或已下架爬虫',
  963. type: 'error',
  964. duration: 3000,
  965. })
  966. }
  967. // 判断爬虫中是否有其他类型,如果有,将其他类型爬虫取消选中
  968. tableEvents.batchListing(list, 'up')
  969. }
  970. const doBatchListing = () => {
  971. ElMessageBox.confirm('确认批量上线?', '提示',
  972. {
  973. customClass: 'j-confirm-message-box',
  974. type: 'warning',
  975. confirmButtonText: '确认',
  976. cancelButtonText: '取消',
  977. showCancelButton: false,
  978. }
  979. ).then(() => {
  980. confirmBatchListing()
  981. })
  982. }
  983. // 执行批量下架操作
  984. const confirmBatchUnListing = () => {
  985. // 过滤一下,找到已上线爬虫,仅提交此类爬虫
  986. const list = listState.selected.filter(v => v.state === 11)
  987. const dropList = listState.selected.filter(v => v.state !== 11)
  988. toggleRow(dropList)
  989. if (list.length <= 0) {
  990. return ElMessage({
  991. message: '至少要选中1条有效数据: 当前未选中已上线爬虫',
  992. type: 'error',
  993. duration: 3000,
  994. })
  995. }
  996. tableEvents.batchListing(list, 'down')
  997. }
  998. const doUnBatchListing = () => {
  999. ElMessageBox.confirm('确认批量下架?', '提示',
  1000. {
  1001. customClass: 'j-confirm-message-box',
  1002. type: 'warning',
  1003. confirmButtonText: '确认',
  1004. cancelButtonText: '取消',
  1005. showCancelButton: false,
  1006. }
  1007. ).then(() => {
  1008. confirmBatchUnListing()
  1009. })
  1010. }
  1011. //Wails事件绑定
  1012. EventsOn("spiderConfigChange", data => {
  1013. const { key, css, url } = data
  1014. refreshAndAsyncEditDialog(key, css)
  1015. // 当触发修改时候,同步给客服端一份
  1016. // if (currentEditRow.value && Object.keys(currentEditRow.value).length <= 0) {
  1017. // const mark = getMarkWithRow(currentEditRow.value)
  1018. // ServerActionCurrentOpenTab(mark)
  1019. // }
  1020. })
  1021. </script>
  1022. <style lang="scss" scoped>
  1023. ::v-deep {
  1024. .el-button {
  1025. &.active {
  1026. background-color: var(--el-button-hover-bg-color);
  1027. border-color: var(--el-button-hover-border-color);
  1028. color: var(--el-button-hover-text-color);
  1029. outline: none;
  1030. }
  1031. }
  1032. }
  1033. .pagination-container {
  1034. display: flex;
  1035. align-items: center;
  1036. justify-content: flex-end;
  1037. }
  1038. .action-bar-container {
  1039. display: flex;
  1040. justify-content: space-between;
  1041. }
  1042. .action-bar-item-container {
  1043. }
  1044. .action-bar-item {
  1045. display: flex;
  1046. align-items: center;
  1047. }
  1048. .action-bar-name {
  1049. font-size: 14px;
  1050. white-space: nowrap;
  1051. }
  1052. </style>