Table.vue 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  1. <!--
  2. * @Author: fxs bjnsfxs@163.com
  3. * @Date: 2024-08-20 18:16:18
  4. * @LastEditors: fxs bjnsfxs@163.com
  5. * @LastEditTime: 2024-11-27
  6. * @FilePath: \SqueezeTheBusc:\Users\NINGMEI\Desktop\Manage\Game-Backstage-Management-System\src\components\Table.vue
  7. * @Description:
  8. *
  9. -->
  10. <script setup lang="ts">
  11. import type { PropsParams, TablePaginationSetting } from '@/types/table'
  12. import type { ReqConfig } from '@/types/dataAnalysis'
  13. import type { TableFieldInfo } from '@/types/table'
  14. import type { FormInstance } from 'element-plus'
  15. import { FilterType, FieldSpecialEffectType } from '@/types/table'
  16. import { initLoadResouce } from '@/utils/resource'
  17. import { fuzzySearch } from '@/utils/common'
  18. import { throttleFunc } from '@/utils/common'
  19. import { computed, onMounted, reactive, ref, toRaw, watch } from 'vue'
  20. import { useTable } from '@/hooks/useTable'
  21. import { useRequest } from '@/hooks/useRequest'
  22. import FilterPopover from './toolsBtn/FilterPopover.vue'
  23. import RegreshBtn from './toolsBtn/RegreshBtn.vue'
  24. import axiosInstance from '@/utils/axios/axiosInstance'
  25. const { analysisResCode } = useRequest()
  26. // 节流的延迟时间
  27. const throttleTime = 500
  28. // 表格工具图标大小
  29. const toolsIconSize = ref(25)
  30. // 查询表单
  31. const queryFormRef = ref<FormInstance>()
  32. // 传过来的配置
  33. const props = withDefaults(defineProps<PropsParams>(), {
  34. needRowindex: true,
  35. needAverage: false,
  36. needLeftTools: false,
  37. needRightTools: false,
  38. openFilterQuery: false,
  39. openPageQuery: false,
  40. needUpload: false,
  41. needDownLoad: false,
  42. openRemoteinquiry: false
  43. })
  44. // 父组件触发的方法
  45. // 删除了一个事件触发,loadSuccess
  46. const emits = defineEmits(['addNewItem', 'upload', 'downLoad'])
  47. // 加载动画
  48. const loading = ref(false)
  49. // 表格数据
  50. const tableData: Array<any> = reactive([])
  51. // 备份表格数据,用于在不分页查询的时候,恢复数据
  52. const backupTableData: Array<any> = []
  53. // 查询表单的数据
  54. const queryFormData = reactive<{ [key: string]: any }>({})
  55. const backupQueryFormData = reactive<{ [key: string]: any }>({})
  56. // 备份其他的请求信息
  57. let backupReqOtherOptions = {}
  58. // 分页数据
  59. const paginationConfig = reactive<TablePaginationSetting>({
  60. currentPage: 1,
  61. limit: 0,
  62. total: 0,
  63. pagesizeList: []
  64. })
  65. // 请求配置
  66. const reqconfig = reactive<ReqConfig>({
  67. url: '',
  68. otherOptions: {}
  69. })
  70. // 资源的加载路径
  71. const resourceInfo: Record<string, string> = {
  72. defaultHead: `/img/default/defaultHead.png`
  73. }
  74. // 使用blob的资源路径信息
  75. const blobUrlInfo = reactive<Record<string, string>>({})
  76. // 一些公用方法
  77. const { getTableData } = useTable(tableData, paginationConfig)
  78. // 没有开启分页查询的时候使用的数据
  79. const tableDataNoPaging = computed(() => {
  80. let curPage = paginationConfig.currentPage
  81. let limit = paginationConfig.limit
  82. let begin = curPage * limit - limit
  83. //这里不减一是因为,slice方法裁切是左闭右开数组
  84. let end = curPage * limit
  85. return tableData.slice(begin, end)
  86. })
  87. // 所有类型为input的表单控件信息
  88. const inputFieldsList = computed(() => {
  89. return props.queryInfo?.filter((item) => item.type === FilterType.INPUT)
  90. })
  91. // 所有类型为select的表单控件信息
  92. const selectFieldsList = computed(() => {
  93. return props.queryInfo?.filter((item) => {
  94. return item.type === FilterType.SELECT
  95. })
  96. })
  97. // 所有类型为date的表单控件信息
  98. const dateFieldsList = computed(() => {
  99. return props.queryInfo?.filter((item) => item.type === FilterType.DATE)
  100. })
  101. // 计算行号
  102. const computedRowIndex = (index: number) => {
  103. return (paginationConfig.currentPage - 1) * paginationConfig.limit + index + 1
  104. }
  105. // 改变页码
  106. const handleCurrentChange = (val: number) => {
  107. paginationConfig.currentPage = val
  108. }
  109. // 改变每页大小
  110. const handleSizeChange = (val: number) => {
  111. paginationConfig.limit = val
  112. }
  113. /**
  114. * @description: 加载表格数据
  115. * @return {*}
  116. */
  117. const loadTableData = async () => {
  118. return new Promise(async (resolve, reject) => {
  119. loading.value = true
  120. // 如果是直接传入的数据
  121. if (props.dataList) {
  122. tableData.splice(0, tableData.length, ...props.dataList)
  123. paginationConfig.total = props.paginationConfig.total
  124. loading.value = false
  125. resolve(true)
  126. } else {
  127. // 如果是需要表格自己请求数据的
  128. if (props.requestConfig) {
  129. try {
  130. // 如果开启了分页查询,那么要计算出需要展示的页码位置所对应的偏移量
  131. // 同时要将查询的条数改为对应的用户选择的展示条数
  132. if (props.openPageQuery) {
  133. reqconfig.otherOptions.offset =
  134. (paginationConfig.currentPage - 1) * paginationConfig.limit
  135. reqconfig.otherOptions.limit = paginationConfig.limit
  136. }
  137. await getTableData(reqconfig.url, reqconfig.otherOptions, props.openPageQuery)
  138. backupTableData.splice(0, backupTableData.length, ...tableData)
  139. resolve(true)
  140. } catch (err) {
  141. console.log(err)
  142. reject(err)
  143. } finally {
  144. loading.value = false
  145. }
  146. } else {
  147. loading.value = false
  148. throw new Error('no match requestConfig')
  149. }
  150. }
  151. })
  152. }
  153. /**
  154. * @description: 获取数据,如果没有直接传入数据,则去请求数据,有则直接用
  155. * @return {*}
  156. */
  157. const getData = () => {
  158. return new Promise(async (resolve, reject) => {
  159. try {
  160. // 等待数据加载完成
  161. await loadTableData()
  162. if (props.needAverage) {
  163. let rowData: any = {}
  164. let oldList: Array<any> = JSON.parse(JSON.stringify(tableData))
  165. Object.values(props.tableFieldsInfo).map((item: TableFieldInfo, index: number) => {
  166. let sum = oldList
  167. .map((item) => item.count)
  168. .reduce((accumulator, currentValue) => accumulator + currentValue, 0)
  169. let averageList = oldList
  170. .map((val) => val[item.name])
  171. .filter((item) => item !== undefined)
  172. if (index === 0) rowData[item.name] = '均值'
  173. else if (item.name === 'count') rowData[item.name] = sum
  174. else {
  175. let num =
  176. averageList.reduce((accumulator, currentValue) => accumulator + currentValue, 0) /
  177. averageList.length
  178. rowData[item.name] = isNaN(num) ? 0 : num.toFixed(2)
  179. }
  180. })
  181. insertRow(0, rowData)
  182. }
  183. resolve(true)
  184. } catch (err) {
  185. console.log(err)
  186. reject(err)
  187. }
  188. })
  189. }
  190. // 包装一下获取数据
  191. const throttleGetData = throttleFunc(getData, 1000)
  192. /**
  193. * @description: 清空表格数据
  194. * @return {*}
  195. */
  196. const resetTableData = () => {
  197. tableData.splice(0, tableData.length)
  198. }
  199. /**
  200. * @description: 按条件查询,如果开启了分页查询,那么会直接重新查询数据,否则,会根据现有数据进行查询
  201. * @return {*}
  202. */
  203. const queryTableData = () => {
  204. if (props.openRemoteinquiry && props.requestConfig) {
  205. reqconfig.otherOptions = { ...props.requestConfig.otherOptions, ...queryFormData }
  206. // 需要在查询前清除掉目前的数据,不然会导致之前缓存的数据混入
  207. // 比如第一页已经缓存了,在第二页重新查询,在切回第一页,还是显示查询前的数据,因为缓存没被清除
  208. tableData.splice(0, tableData.length)
  209. getData()
  210. } else {
  211. let filteredTable = []
  212. // 过滤出来所有符合formData数据的条件
  213. filteredTable = backupTableData.filter((item) => {
  214. let state = true
  215. for (let [k, v] of Object.entries(queryFormData)) {
  216. // 模糊查询,看值是否跟表格中的数据匹配
  217. if (!fuzzySearch(v, item[k])) {
  218. state = false
  219. break
  220. }
  221. }
  222. return state
  223. })
  224. paginationConfig.total = filteredTable.length
  225. tableData.splice(0, tableData.length, ...filteredTable)
  226. }
  227. }
  228. // 把查询方法包装一下,节流
  229. const throttleQueryTableData = throttleFunc(queryTableData, throttleTime)
  230. /**
  231. * 重置查询表单参数
  232. */
  233. const resetQueryFormData = () => {
  234. let data = JSON.parse(
  235. JSON.stringify(backupQueryFormData, (_, v) => (typeof v === 'undefined' ? '' : v))
  236. )
  237. Object.assign(queryFormData, data)
  238. reqconfig.otherOptions = backupReqOtherOptions // 要把请求的参数也重置一次,不然切换平台等操作,会带着原来的查询参数请求
  239. }
  240. /**
  241. * @description: 重置整个查询表单,重置后,再请求一次全部表格
  242. * @param {*} formEl 表单对象
  243. * @return {*}
  244. */
  245. const resetQueryForm = () => {
  246. resetQueryFormData()
  247. queryTableData()
  248. }
  249. // 把重置方法包装一下,节流
  250. const throttleResetQueryForm = throttleFunc(resetQueryForm, throttleTime)
  251. /**
  252. * @description: 在获取完数据后,插入均值行
  253. * @param {*} start 插入的位置
  254. * @param {*} rowData 插入的数据
  255. * @return {*}
  256. */
  257. const insertRow = (start: number, rowData: any) => {
  258. if (props.openPageQuery) {
  259. tableData[start].splice(0, 0, rowData)
  260. } else {
  261. tableData.splice(start, 0, rowData)
  262. }
  263. }
  264. /**
  265. * @description: 根据计算出来的值去返回对应的颜色深度
  266. * @return {*}
  267. */
  268. const getDecimalFromRange = (number: number) => {
  269. if (number === null || number === undefined) return 0
  270. if (number < 25) return 0.25
  271. else if (number < 50) return 0.5
  272. else if (number < 75) return 0.75
  273. else return 1.0 // 如果number >= 75,则直接返回1.00
  274. }
  275. /**
  276. * @description: 单独处理拥有均值行的表格每个单元格的样式,均值字段均加粗,其他需要比较的字段根据自身百分比显示颜色
  277. * 其中使用row-style无效,scope会导致无法覆盖样式
  278. * 同时由于我自定义了表格内容,哪里的样式会覆盖row的样式,所以只能单独对单元格设置
  279. * @param {*} info 每个单元格的信息
  280. * @return {*}
  281. */
  282. const tableCellStyle = (info: any) => {
  283. if (info.row.date === '均值')
  284. return {
  285. 'font-weight': 'bold'
  286. }
  287. else if (info.column.property != 'count' && info.column.property != 'date' && props.needAverage) {
  288. return {
  289. 'background-color': `rgba(59, 157, 247,${getDecimalFromRange(info.row[info.column.property])})`
  290. }
  291. } else return {}
  292. }
  293. /**
  294. * @description: 监听litmit,currentpage的变化,改变后去重新请求数据
  295. * 如果是limit的变化,则需要把当前页置为1
  296. * @return {*}
  297. */
  298. const watchLimit = watch(
  299. () => paginationConfig.limit,
  300. (newLimit, oldLimit) => {
  301. // resetQueryForm(false)
  302. if (newLimit !== oldLimit) {
  303. resetQueryFormData()
  304. // 需要给分页按钮加上:current-page.sync="current_page" 配置,不然不生效
  305. // 当改变每页大小时把之前的缓存全部清除,重新开始
  306. if (paginationConfig.currentPage !== 1) {
  307. paginationConfig.currentPage = 1
  308. } else {
  309. getData()
  310. }
  311. }
  312. },
  313. { deep: true }
  314. )
  315. /**
  316. * 监听currentpage的变化,如果开启了分页查询,并且当前页发生变化,并且当前页的数据不存在,则重新请求数据
  317. */
  318. const watchCurPage = watch(
  319. () => paginationConfig.currentPage,
  320. (newPage, oldPage) => {
  321. if (newPage !== oldPage && !tableData[newPage]) {
  322. getData()
  323. }
  324. },
  325. {
  326. deep: true
  327. }
  328. )
  329. // 如果没有开启分页查询,直接关闭这两个监听
  330. if (!props.openPageQuery) {
  331. watchLimit()
  332. watchCurPage()
  333. }
  334. /**
  335. * @description: 监听gid的变化,重新请求数据
  336. * @return {*}
  337. */
  338. watch(
  339. () => props.requestConfig?.otherOptions.gid,
  340. (newGid, oldGid) => {
  341. if (newGid != oldGid) {
  342. // resetQueryForm(false)
  343. resetQueryFormData()
  344. reqconfig.otherOptions.gid = newGid
  345. getData()
  346. }
  347. },
  348. { deep: true }
  349. )
  350. // 监听pf
  351. const watchPf = watch(
  352. () => props.requestConfig?.otherOptions.pf,
  353. (newPf, oldPf) => {
  354. if (newPf != oldPf) {
  355. // resetQueryForm(false)
  356. resetQueryFormData()
  357. reqconfig.otherOptions.pf = newPf
  358. getData()
  359. }
  360. },
  361. { deep: true }
  362. )
  363. // 监听传入的datalist的变化,然后去更新数据
  364. const changeDataList = watch(
  365. () => [props.dataList],
  366. () => {
  367. // resetQueryForm(false)
  368. resetQueryFormData()
  369. getData()
  370. },
  371. {
  372. deep: true
  373. }
  374. )
  375. // 监听日期的变化,
  376. const watchDateChange = watch(
  377. () => [props.requestConfig?.otherOptions.startTime, props.requestConfig?.otherOptions.endTime],
  378. () => {
  379. // resetQueryForm(false)
  380. resetQueryFormData()
  381. getData()
  382. },
  383. { deep: true }
  384. )
  385. /**
  386. * @description: 创建row-key优化表格性能
  387. * @return {*}
  388. */
  389. const createRowKey = () => {
  390. return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
  391. }
  392. // 没有pf取消掉
  393. if (!props.requestConfig?.otherOptions.pf) watchPf()
  394. //如果没有日期就取消掉
  395. if (!props.requestConfig?.otherOptions.startTime && !props.requestConfig?.otherOptions.endTime) {
  396. watchDateChange()
  397. }
  398. // 没传入datalist则取消该监听
  399. if (!props.dataList) {
  400. changeDataList()
  401. }
  402. /**
  403. * @description: 拷贝一份配置文件
  404. * @return {*}
  405. */
  406. const initpageConfig = () => {
  407. Object.assign(paginationConfig, props.paginationConfig)
  408. }
  409. /**
  410. * @description: 初始化请求配置,用于把拷贝一份新的数据
  411. * @return {*}
  412. */
  413. const initReqConfig = () => {
  414. Object.assign(reqconfig, props.requestConfig)
  415. }
  416. /**
  417. * @description: 初始化查询框的数据
  418. * @return {*}
  419. */
  420. const initFormData = () => {
  421. props.queryInfo?.map((item: any) => {
  422. queryFormData[item.name] = item.default
  423. backupQueryFormData[item.name] = item.default
  424. })
  425. // backupQueryFormData = JSON.parse(JSON.stringify(queryFormData))
  426. Object.assign(backupQueryFormData, JSON.parse(JSON.stringify(queryFormData)))
  427. }
  428. /**
  429. * @description: 表格排序
  430. * @param {*} data 获取到的数据
  431. * @return {*}
  432. */
  433. const tableSortChange = (data: { column: any; prop: string; order: any }) => {
  434. // resetQueryForm(false)
  435. resetQueryFormData()
  436. let { order } = { ...data }
  437. if (order === 'ascending') order = 'asc'
  438. else if (order === 'descending') order = 'desc'
  439. else order = ''
  440. reqconfig.otherOptions.order = order
  441. getData()
  442. }
  443. /**
  444. * @description: 删除行
  445. * @param {*} url 请求地址
  446. * @param {*} row 行数据
  447. * @return {*}
  448. */
  449. const deleteRow = (url: string, filedsInfo: any) => {
  450. axiosInstance
  451. .post(url, { ...filedsInfo })
  452. .then((data) => {
  453. analysisResCode(data).then(() => {
  454. // resetQueryForm(false)
  455. resetQueryFormData()
  456. getData()
  457. })
  458. })
  459. .catch((err) => {
  460. console.log(err)
  461. })
  462. }
  463. /**
  464. * @description: 下载表格数据
  465. * @return {*}
  466. */
  467. const downLoadTable = () => {
  468. emits('downLoad', JSON.parse(JSON.stringify(tableData)))
  469. }
  470. /**
  471. * @description: 外部获取数据
  472. * @return {*}
  473. */
  474. const outGetTableData = () => {
  475. return toRaw(tableData).flat()
  476. }
  477. const numberInput = (key: string) => {
  478. let val = queryFormData[key]
  479. val = parseInt(val)
  480. if (isNaN(val)) {
  481. val = 0
  482. }
  483. queryFormData[key] = val
  484. }
  485. // 定义暴露出去的方法
  486. defineExpose({
  487. getData,
  488. resetTableData,
  489. deleteRow,
  490. downLoadTable,
  491. outGetTableData,
  492. resetQueryForm
  493. })
  494. onMounted(() => {
  495. initpageConfig()
  496. initReqConfig()
  497. initFormData()
  498. backupReqOtherOptions = reqconfig.otherOptions // 备份一份请求参数
  499. // registerWatchProps()
  500. if (props.loadingState !== undefined) {
  501. loading.value = props.loadingState
  502. }
  503. // 这里会造成有的table页第一次进来刷新两次,因为有的页面gid或者平台等数据会变化,导致请求第二次
  504. // 但是这里又必须请求一次,因为有的页面数据是没有变化的
  505. getData()
  506. // 去加载所有需要的资源
  507. initLoadResouce(resourceInfo).then((data) => {
  508. Object.assign(blobUrlInfo, data)
  509. })
  510. })
  511. </script>
  512. <template>
  513. <div class="tableContent">
  514. <div class="filterBox" v-if="openFilterQuery">
  515. <!-- slot -->
  516. <div class="filterHeader">
  517. <span>查询条件</span>
  518. </div>
  519. <div class="filterBody">
  520. <el-form
  521. :inline="true"
  522. ref="queryFormRef"
  523. :model="queryFormData"
  524. class="queryForm"
  525. :label-position="'left'"
  526. >
  527. <template v-for="item in inputFieldsList">
  528. <!-- 所有的input查询框 -->
  529. <el-form-item
  530. @keyup.enter.native="throttleQueryTableData"
  531. :label="item.label"
  532. v-if="item.valueType !== 'int'"
  533. class="filterItem"
  534. >
  535. <el-input
  536. clearable
  537. :placeholder="item.placeholder"
  538. v-model="queryFormData[item.name]"
  539. />
  540. </el-form-item>
  541. <el-form-item
  542. @keyup.enter.native="throttleQueryTableData"
  543. :label="item.label"
  544. v-else
  545. class="filterItem"
  546. >
  547. <el-input
  548. clearable
  549. v-model="queryFormData[item.name]"
  550. @input="numberInput(item.name)"
  551. :placeholder="item.placeholder"
  552. />
  553. </el-form-item>
  554. </template>
  555. <!-- 所有选择框 -->
  556. <!-- <el-config-provider :value-on-clear="null" :empty-values="[undefined, null]"> -->
  557. <el-form-item :label="item.label" v-for="item in selectFieldsList" class="filterItem">
  558. <el-select
  559. :empty-values="[undefined, null]"
  560. v-model="queryFormData[item.name]"
  561. :placeholder="item.placeholder"
  562. :value-key="item.name"
  563. >
  564. <el-option v-for="val in item.otherOption" :label="val.cnName" :value="val.value" />
  565. </el-select>
  566. </el-form-item>
  567. <!-- </el-config-provider> -->
  568. <!-- 所有日期选择框 -->
  569. <el-form-item :label="item.label" v-for="item in dateFieldsList" class="filterItem">
  570. <el-date-picker
  571. v-model="queryFormData[item.name]"
  572. type="date"
  573. :placeholder="item.placeholder"
  574. clearable
  575. />
  576. </el-form-item>
  577. </el-form>
  578. <!-- 分割线 -->
  579. <!-- <el-divider class="queryPartition" content-position="center" direction="vertical" /> -->
  580. <div class="queryBox">
  581. <el-divider class="queryPartition" content-position="center" direction="vertical" />
  582. <div class="queryBtnBox">
  583. <el-button class="queryBtn" color="#165dff" @click="throttleQueryTableData">
  584. <el-icon><Search /></el-icon>查询
  585. </el-button>
  586. <el-button class="refreshBtn" color="#f2f3f5" @click="throttleResetQueryForm()">
  587. <el-icon><RefreshRight /></el-icon>重置
  588. </el-button>
  589. </div>
  590. </div>
  591. </div>
  592. </div>
  593. <!-- 分割线 -->
  594. <!-- <el-divider class="partition" content-position="center" /> -->
  595. <div class="tableTools">
  596. <div class="leftTools">
  597. <div class="leftToolsGroup" v-if="needLeftTools" style="display: flex">
  598. <el-button class="leftToolBtn" color="#165dff" @click="emits('addNewItem')">
  599. <el-icon><Plus /></el-icon>新增
  600. </el-button>
  601. <el-button
  602. class="leftToolBtn"
  603. color="#626aef"
  604. @click="emits('upload', outGetTableData())"
  605. v-if="needUpload"
  606. >
  607. <el-icon><Upload /></el-icon>上传
  608. </el-button>
  609. </div>
  610. </div>
  611. <div class="rightTools" v-if="needRightTools">
  612. <el-button
  613. v-if="needDownload"
  614. color="#f0f1f3"
  615. size="default"
  616. class="rightToolsItem"
  617. @click="downLoadTable"
  618. >
  619. <el-icon><Download /></el-icon>下载
  620. </el-button>
  621. <!-- throttleFunc(queryTableData, 200) -->
  622. <RegreshBtn @refresh-table="throttleGetData" :icon-size="toolsIconSize"></RegreshBtn>
  623. <FilterPopover
  624. :table-fields-info="tableFieldsInfo"
  625. :icon-size="toolsIconSize"
  626. ></FilterPopover>
  627. </div>
  628. </div>
  629. <div class="tableBox">
  630. <!-- 没有分页的时候需要重新计算一下data -->
  631. <el-table
  632. :data="openPageQuery ? tableData[paginationConfig.currentPage] : tableDataNoPaging"
  633. style="width: 100%"
  634. class="tableBody"
  635. :cell-style="tableCellStyle"
  636. v-loading="loading"
  637. :row-key="createRowKey()"
  638. @sort-change="tableSortChange"
  639. >
  640. <el-table-column
  641. v-if="props.needRowindex"
  642. align="center"
  643. label="#"
  644. type="index"
  645. :index="computedRowIndex"
  646. />
  647. <template v-for="item in tableFieldsInfo">
  648. <el-table-column
  649. :prop="item.name"
  650. :label="item.cnName"
  651. :min-width="item.specialEffect?.type === FieldSpecialEffectType.DROPDOWN ? '170px' : ''"
  652. align="center"
  653. show-overflow-tooltip
  654. v-if="item.isShow"
  655. :sortable="item.needSort === true ? 'custorm' : false"
  656. >
  657. <template v-slot="scope">
  658. <!-- tag类 -->
  659. <el-tag
  660. v-if="item.specialEffect?.type === FieldSpecialEffectType.TAG"
  661. :type="scope.row[item.name] ? 'danger' : 'success'"
  662. >
  663. {{
  664. scope.row[item.name]
  665. ? item.specialEffect.othnerInfo.text[0]
  666. : item.specialEffect.othnerInfo.text[1]
  667. }}
  668. </el-tag>
  669. <!-- :src="loadResource(scope.row[item.name])" -->
  670. <!-- :src="scope.row[item.name]" -->
  671. <!-- 头像类 -->
  672. <el-image
  673. v-else-if="item.specialEffect?.type === FieldSpecialEffectType.IMG"
  674. :preview-teleported="true"
  675. :src="scope.row[item.name]"
  676. :preview-src-list="[scope.row[item.name]]"
  677. style="width: 35px; height: 35px"
  678. :fit="'fill'"
  679. :hide-on-click-modal="true"
  680. >
  681. <template #error>
  682. <!-- -->
  683. <img style="width: 35px; height: 35px" :src="blobUrlInfo.defaultHead" />
  684. </template>
  685. </el-image>
  686. <!-- 文字类 -->
  687. <el-text
  688. v-else-if="item.specialEffect?.type === FieldSpecialEffectType.TEXT"
  689. :type="
  690. scope.row[item.name]
  691. ? item.specialEffect.othnerInfo.color[0]
  692. : item.specialEffect.othnerInfo.color[1]
  693. "
  694. >
  695. {{ scope.row[item.name] }}
  696. </el-text>
  697. <!-- 翻译类 -->
  698. <el-text v-else-if="item.specialEffect?.type === FieldSpecialEffectType.TRANSLATE">
  699. <el-icon
  700. v-if="item.specialEffect.othnerInfo.icon"
  701. style="padding-right: 8px"
  702. :color="scope.row[item.name] ? '#409EFF' : '#F56C6C'"
  703. ><icon-tabler-point-filled></icon-tabler-point-filled
  704. ></el-icon>
  705. {{
  706. item.specialEffect.othnerInfo.translateText[scope.row[item.name]]
  707. ? item.specialEffect.othnerInfo.translateText[scope.row[item.name]]
  708. : '未知'
  709. }}
  710. </el-text>
  711. <!-- 状态类 -->
  712. <el-text v-else-if="item.specialEffect?.type === FieldSpecialEffectType.STATE">
  713. <span>
  714. <el-icon
  715. style="padding-right: 8px"
  716. :color="scope.row[item.name] ? '#409EFF' : '#F56C6C'"
  717. ><icon-tabler-point-filled></icon-tabler-point-filled
  718. ></el-icon>
  719. {{
  720. scope.row[item.name]
  721. ? item.specialEffect.othnerInfo.text[0]
  722. : item.specialEffect.othnerInfo.text[1]
  723. }}</span
  724. >
  725. </el-text>
  726. <!-- 开关类 -->
  727. <el-switch
  728. :active-value="1"
  729. :inactive-value="0"
  730. v-else-if="item.specialEffect?.type === FieldSpecialEffectType.SWITCH"
  731. v-model="scope.row[item.name]"
  732. :data="scope.row[item.name]"
  733. size="default"
  734. >
  735. </el-switch>
  736. <!-- 下拉菜单类 -->
  737. <el-dropdown
  738. trigger="click"
  739. v-else-if="item.specialEffect?.type === FieldSpecialEffectType.DROPDOWN"
  740. >
  741. <span
  742. class="el-dropdown-link"
  743. style="display: flex; align-items: center; cursor: pointer"
  744. >
  745. <el-icon
  746. style="padding-right: 8px"
  747. :color="scope.row[item.name] ? '#409EFF' : '#F56C6C'"
  748. ><icon-tabler-point-filled></icon-tabler-point-filled
  749. ></el-icon>
  750. {{
  751. scope.row[item.name]
  752. ? item.specialEffect.othnerInfo.text[0]
  753. : item.specialEffect.othnerInfo.text[1]
  754. }}
  755. <el-icon class="el-icon--right"><arrow-down /></el-icon>
  756. </span>
  757. <template #dropdown>
  758. <el-dropdown-menu>
  759. <el-dropdown-item :command="{ value: true }">使用中</el-dropdown-item>
  760. <el-dropdown-item :command="{ value: false }">已弃用</el-dropdown-item>
  761. </el-dropdown-menu>
  762. </template>
  763. </el-dropdown>
  764. <el-text v-else>
  765. <!-- 其他列按默认方式显示 -->
  766. {{
  767. props.needAverage &&
  768. scope.row[item.name] !== undefined &&
  769. item.name !== 'count' &&
  770. item.name !== 'date'
  771. ? scope.row[item.name] + '%'
  772. : scope.row[item.name]
  773. }}
  774. </el-text>
  775. </template>
  776. </el-table-column>
  777. </template>
  778. <slot name="tableOperation"></slot>
  779. </el-table>
  780. <div class="userTablePaginationBox">
  781. <el-pagination
  782. class="userTablePagination"
  783. background
  784. :page-size="paginationConfig.limit"
  785. :page-sizes="paginationConfig.pagesizeList"
  786. table-layout="fixed"
  787. layout="prev, pager, next ,jumper ,sizes,total,"
  788. :total="paginationConfig.total"
  789. :current-page.sync="paginationConfig.currentPage"
  790. @current-change="handleCurrentChange"
  791. @size-change="handleSizeChange"
  792. />
  793. </div>
  794. </div>
  795. </div>
  796. </template>
  797. <style scoped>
  798. .tableContent {
  799. margin: 10px auto 20px;
  800. width: 100%;
  801. /* height: 100%; */
  802. box-shadow:
  803. 0 4px 8px 0 rgba(0, 0, 0, 0.02),
  804. 0 1px 3px 0 rgba(0, 0, 0, 0.02);
  805. }
  806. .filterBox,
  807. .tableBox {
  808. width: 100%;
  809. }
  810. .filterBox {
  811. margin-top: 1%;
  812. min-height: 18%;
  813. display: flex;
  814. flex-direction: column;
  815. }
  816. .filterHeader,
  817. .filterBody {
  818. width: 98%;
  819. box-sizing: border-box;
  820. margin: 0 auto;
  821. }
  822. .filterHeader {
  823. display: flex;
  824. align-items: center;
  825. color: black;
  826. font-size: 16px;
  827. font-weight: bold;
  828. /* background-color: lightblue; */
  829. /* margin-bottom: 1%; */
  830. }
  831. .filterBody {
  832. display: flex;
  833. padding: 0 24px;
  834. }
  835. .queryBox {
  836. width: 10%;
  837. display: flex;
  838. justify-content: space-around;
  839. }
  840. .queryBtnBox {
  841. width: 100%;
  842. display: flex;
  843. flex-direction: column;
  844. align-items: center;
  845. justify-content: center;
  846. }
  847. .refreshBtn {
  848. margin-top: 10%;
  849. margin-bottom: 10%;
  850. }
  851. .queryPartition {
  852. height: 90%;
  853. }
  854. .queryForm {
  855. width: 90%;
  856. display: flex;
  857. flex-wrap: wrap;
  858. /* justify-content: space-between; */
  859. }
  860. .filterItem {
  861. width: 20%;
  862. display: flex;
  863. align-items: center;
  864. }
  865. .partition {
  866. width: 98%;
  867. margin: 0 auto;
  868. }
  869. .tableTools {
  870. width: 98%;
  871. margin: 0 auto;
  872. display: flex;
  873. justify-content: space-between;
  874. margin-top: 1%;
  875. }
  876. .leftTools,
  877. .rightTools {
  878. width: 10%;
  879. display: flex;
  880. align-items: center;
  881. justify-content: space-between;
  882. }
  883. .rightTools {
  884. width: 5%;
  885. }
  886. .tableBox {
  887. width: 98%;
  888. /* height: 98%; */
  889. margin: 5px auto;
  890. box-shadow:
  891. 0 4px 8px 0 rgba(0, 0, 0, 0.02),
  892. 0 1px 3px 0 rgba(0, 0, 0, 0.02);
  893. /* margin-top: 0.5%;
  894. margin-bottom: 2%; */
  895. }
  896. .tableBody {
  897. box-shadow: 0 0 3px 0px rgba(0, 0, 0, 0.1);
  898. /* box-shadow:
  899. -4px -4px 8px 1px rgba(0, 0, 0, 0.02),
  900. -4px -4px 3px 1px rgba(0, 0, 0, 0.02); */
  901. }
  902. .userTablePaginationBox {
  903. box-sizing: border-box;
  904. width: 98%;
  905. margin: 0% auto;
  906. padding: 1% 0;
  907. display: flex;
  908. justify-content: center;
  909. }
  910. .averageItem {
  911. font-size: 14px;
  912. color: #515b6f;
  913. }
  914. .normalItem {
  915. font-size: 14px;
  916. color: #515b6f;
  917. font-weight: 400;
  918. }
  919. .leftToolBtn {
  920. margin-right: 5px;
  921. }
  922. </style>