image.png
    现在要加个出库按钮,打开出库页模态框

    1. <template>
    2. <div class="query-view-detail">
    3. <DetailHeader
    4. @saveModalHandleOpen="saveModalHandleOpen"
    5. @powerModalHandleOpen="powerModalHandleOpen"
    6. @deleteModalHandleOpen="deleteModalHandleOpen"
    7. @handleSelectWarnTypes="handleSelectWarnTypes"
    8. @exportExcel="exportExcel"
    9. />
    10. <main>
    11. <SearchBar
    12. ref="searchBarRef"
    13. @handleSearch="handleSearch"
    14. @updateFilter="updateFilter"
    15. :filter="filter"
    16. :frameFilters="frameFilters"
    17. :currentTab="currentTab"
    18. />
    19. <!-- <ResidualQuantityWarn @handleClickWatchQuantityWarn="handleClickWatchQuantityWarn" /> -->
    20. <TableList
    21. ref="tableListRef"
    22. :loading="refreshTable.loading"
    23. @changeTab="changeTab"
    24. @changePagination="changePagination"
    25. :currentTab="currentTab"
    26. :tableListData="tableListData"
    27. @openStockInModal="openStockInModal"
    28. @openStockOutModal="openStockOutModal"
    29. />
    30. </main>
    31. <!-- Modals -->
    32. <SaveModal
    33. ref="saveModalRef"
    34. :filter="filter"
    35. @saveModalMakeViewHandleOk="saveModalMakeViewHandleOk"
    36. @saveModalUpdateViewHandleOk="saveModalUpdateViewHandleOk"
    37. />
    38. <PowerModal ref="powerModalRef" @powerModalHandleOk="powerModalHandleOk" />
    39. <DeleteModal ref="deleteModalRef" @deleteModalHandleOk="deleteModalHandleOk" />
    40. <StockInModal v-model:visible="stockInModalVisible" @refreshCurrentTabTable="refreshCurrentTabTable" />
    41. <!-- 出库组件,通过v-model控制显隐-->
    42. <StockOutModal v-model:visible="stockOutModalVisible" @refreshCurrentTabTable="refreshCurrentTabTable" />
    43. </div>
    44. </template>
    45. <script lang="ts">
    46. export type FrameFilter = {
    47. fieldId: string | null
    48. fieldKey?: string
    49. fieldName: string
    50. fieldType: FilterOriginType
    51. filterType: TableColsTypeEnum
    52. preDefinedValues: string[]
    53. }
    54. </script>
    55. <script setup lang="ts">
    56. //components
    57. import TableList, { MaterialData } from './components/TableList.vue'
    58. import DetailHeader from './components/DetailHeader.vue'
    59. import SaveModal from './components/SaveModal.vue'
    60. import PowerModal from './components/PowerModal.vue'
    61. import DeleteModal from './components/DeleteModal.vue'
    62. import SearchBar from './components/SearchBar.vue'
    63. import { Message } from '@arco-design/web-vue'
    64. // import ResidualQuantityWarn from './components/ResidualQuantityWarn.vue'
    65. import StockInModal from './components/stockInModal/index.vue'
    66. import StockOutModal from './components/stockOutModal/index.vue'
    67. //utils
    68. import { ref, reactive, computed, inject, toRaw, shallowRef, provide } from 'vue'
    69. import {
    70. initSearchInfo,
    71. TabsEnum,
    72. buildColumns,
    73. TreeNodeTypeEnum,
    74. deBuildColumns,
    75. SaveViewModeEnmu
    76. } from '@/views/register/registration-and-batch-info-query/utils'
    77. import { useFetch } from '@/hooks'
    78. //api
    79. import {
    80. searchBatchInfoList,
    81. searchProductInfoList,
    82. updateSearchView,
    83. saveSearchView,
    84. deleteSearchView,
    85. getProductFrameFilters,
    86. getExportColumnsData,
    87. getExportProd
    88. } from '@/api/material'
    89. //types
    90. import { Filter, FilterOriginType, SearchInfo, TableColsTypeEnum } from '../types'
    91. import { exportExcelModeEnum } from '@/views/register/registration-and-batch-info-query/types'
    92. import { SearchTypeEnmu } from '@/common/enum'
    93. //plugin
    94. import { useI18n } from 'vue-i18n'
    95. import _, { cloneDeep } from 'lodash'
    96. const { t } = useI18n()
    97. const RootData = inject('rootData')
    98. const { treeNodeData } = RootData as any
    99. const Emiter = defineEmits(['saveView', 'updateView', 'deleteView', 'powerModalHandleOk'])
    100. const globalSearchInfo = ref(initSearchInfo())
    101. const setGlobalSearchInfo = (searchInfo) => (globalSearchInfo.value = searchInfo)
    102. const tableListRef = ref()
    103. const stockInModalVisible = ref<boolean>(false)
    104. const stockOutModalVisible = ref<boolean>(false)
    105. const materialDataList = shallowRef<MaterialData[] | undefined>(void 0)
    106. provide('materialDataList', materialDataList)
    107. const openStockInModal = (data: MaterialData[]) => {
    108. stockInModalVisible.value = true
    109. materialDataList.value = data
    110. }
    111. const openStockOutModal = (data: MaterialData[]) => {
    112. stockOutModalVisible.value = true
    113. materialDataList.value = data
    114. }
    115. const searchBarRef = ref()
    116. /**
    117. * DetailHeader 相关业务
    118. */
    119. const saveModalRef = ref()
    120. const powerModalRef = ref()
    121. const deleteModalRef = ref()
    122. const saveModalHandleOpen = () => {
    123. saveModalRef.value.saveModalHandleOpen(SaveViewModeEnmu.CREATE_VIEW, {})
    124. }
    125. const powerModalHandleOpen = () => {
    126. powerModalRef.value.powerModalHandleOpen()
    127. }
    128. const deleteModalHandleOpen = () => {
    129. deleteModalRef.value.deleteModalHandleOpen()
    130. }
    131. const handleSelectWarnTypes = async (selectWarnTypes) => {
    132. const newFilter = Object.assign(globalSearchInfo.value.filter, { alarmTypes: selectWarnTypes })
    133. const searchInfo = globalSearchInfo.value
    134. await refreshAllTabTable({ searchInfo })
    135. }
    136. /**
    137. * 根据不同的tab,映射不同的接口
    138. */
    139. const refreshTabTableMaps = {
    140. [TabsEnum.REGISTER]: async ({ searchInfo, allColumns }: { searchInfo: SearchInfo; allColumns?: any[] }) => {
    141. let ProductTableInfo = await searchProductInfoList(searchInfo)
    142. //拼接展示列的信息
    143. // if (!isEmpty(allColumns)) {
    144. // ProductTableInfo.allColumns = buildColumns(TabsEnum.REGISTER, allColumns as any[])
    145. // ProductTableInfo.showColumns = buildColumns(TabsEnum.REGISTER, searchInfo.productShowCols)
    146. // } else {
    147. // ProductTableInfo.allColumns = buildColumns(TabsEnum.REGISTER, searchInfo.productShowCols)
    148. // ProductTableInfo.showColumns = buildColumns(TabsEnum.REGISTER, searchInfo.productShowCols)
    149. // }
    150. ProductTableInfo.allColumns = buildColumns(TabsEnum.REGISTER, searchInfo.productShowCols)
    151. ProductTableInfo.data = parseCustomParams(ProductTableInfo.data)
    152. tableListData[TabsEnum.REGISTER] = ProductTableInfo
    153. setGlobalSearchInfo(cloneDeep(toRaw(searchInfo)))
    154. filter.value = cloneDeep(toRaw(searchInfo.filter))
    155. },
    156. [TabsEnum.BATCH]: async ({ searchInfo, allColumns }: { searchInfo: SearchInfo; allColumns?: any[] }) => {
    157. let BatchTableInfo = await searchBatchInfoList(searchInfo)
    158. //拼接展示列的信息
    159. BatchTableInfo.allColumns = buildColumns(TabsEnum.BATCH, searchInfo.batchShowCols)
    160. BatchTableInfo.data = parseCustomParams(BatchTableInfo.data)
    161. tableListData[TabsEnum.BATCH] = BatchTableInfo
    162. setGlobalSearchInfo(cloneDeep(toRaw(searchInfo)))
    163. filter.value = cloneDeep(toRaw(searchInfo.filter))
    164. }
    165. }
    166. //将一些自定义字段解析到每个 dataItem 的外部
    167. function parseCustomParams(data) {
    168. return data.map((row, index) => {
    169. let batchParams = row?.batchParams || []
    170. let productParams = row?.productParams || []
    171. const params = [...batchParams, ...productParams]
    172. params.map((v) => {
    173. row[v.fieldId] = v
    174. })
    175. //标记序号
    176. row.index = index + 1
    177. return row
    178. })
    179. }
    180. /**
    181. * 刷新所有tab数据
    182. */
    183. const refreshAllTabTable = async ({ searchInfo, allColumns }: { searchInfo: SearchInfo; allColumns?: any[] }) => {
    184. refreshTable.value.loading.value = true
    185. tableListRef.value.clearCacheData() //清除之前表单的缓存数据
    186. await Promise.all(
    187. Object.values(refreshTabTableMaps).map((refreshFunc) => refreshFunc({ searchInfo, allColumns }))
    188. ).finally(() => {
    189. refreshTable.value.loading.value = false
    190. })
    191. //回显对应的 keyword
    192. searchBarRef.value.setKeywrod(searchInfo.keyword)
    193. }
    194. /**
    195. * 刷新当前tab数据
    196. */
    197. const refreshCurrentTabTable = () => {
    198. refreshTabTableMaps[currentTab.value]({ searchInfo: globalSearchInfo.value })
    199. tableListRef.value.initialSelectedRowKeys()
    200. }
    201. /**
    202. * tab 相关
    203. */
    204. const currentTab = reactive<{ value: TabsEnum }>({ value: TabsEnum.REGISTER })
    205. // computed返回的是ref,之后访问时需要通过value
    206. const refreshTable = computed(() => {
    207. return useFetch(refreshTabTableMaps[currentTab.value], {
    208. isLazy: true
    209. })
    210. })
    211. // 切换tab事件
    212. const changeTab = async ({ type, paginationInfo }) => {
    213. currentTab.value = Number(type)
    214. const { pageNum, pageSize } = paginationInfo
    215. await refreshTable.value.fetchResource({ searchInfo: Object.assign(globalSearchInfo.value, { pageNum, pageSize }) })
    216. }
    217. //表单总数
    218. const tableListData = reactive({
    219. [TabsEnum.REGISTER]: {},
    220. [TabsEnum.BATCH]: {}
    221. })
    222. const filter = ref<Filter>({})
    223. /**
    224. * 搜索按钮事件
    225. * TODO
    226. * 根据筛选条件进行搜索
    227. */
    228. const handleSearch = ({ keyword = '' }: { keyword: string }) => {
    229. // console.log('搜索', keyWord, tableListRef.value.selectedKeys)
    230. //根据不同的节点类型,拿到正确的frameId
    231. let frameId
    232. switch (treeNodeData.value.nodeType) {
    233. case TreeNodeTypeEnum.PRODUCT_FRAM: {
    234. frameId = treeNodeData.value.id
    235. }
    236. case TreeNodeTypeEnum.VIEW: {
    237. frameId = treeNodeData.value.parentId
    238. }
    239. default: {
    240. frameId = treeNodeData.value.id
    241. }
    242. }
    243. //初始化分页数据
    244. tableListRef.value.initialPresetPagination()
    245. const pageNum = tableListRef.value.paginationInfo.pageNum
    246. //刷新列表
    247. const targetParams = { ...globalSearchInfo.value, keyword, pageNum }
    248. refreshTable.value.fetchResource({ searchInfo: targetParams })
    249. //清空选择列
    250. tableListRef.value.initialSelectedRowKeys()
    251. }
    252. /**
    253. * 筛选条件更新到globalSearchInfo
    254. */
    255. const updateFilter = (filter: Filter) => {
    256. Object.assign(globalSearchInfo.value.filter, filter)
    257. }
    258. /**
    259. * 导出接口
    260. */
    261. const exportInfo = {
    262. [TabsEnum.REGISTER]: {
    263. method: getExportProd,
    264. prefixName: t('register.registrationAndBatchInfoQuery.queryViewDetail.product')
    265. },
    266. [TabsEnum.BATCH]: {
    267. method: getExportColumnsData,
    268. prefixName: t('register.registrationAndBatchInfoQuery.queryViewDetail.batch')
    269. }
    270. }
    271. /**
    272. * 导出
    273. */
    274. const exportExcel = async (mode, callback: Function) => {
    275. let condition = {}
    276. let seletCols: {
    277. productShowCols: any[]
    278. batchShowCols: any[]
    279. } = {
    280. productShowCols: [],
    281. batchShowCols: []
    282. }
    283. switch (mode) {
    284. case exportExcelModeEnum.CURRENT_SHOW: {
    285. const { productShowCols, batchShowCols } = tableListRef.value.showCols
    286. seletCols.productShowCols = deBuildColumns(productShowCols)
    287. seletCols.batchShowCols = deBuildColumns(batchShowCols)
    288. condition = Object.assign({}, globalSearchInfo.value, seletCols)
    289. break
    290. }
    291. default: {
    292. condition = Object.assign({}, globalSearchInfo.value, tableListRef.value.currentAllCols)
    293. break
    294. }
    295. }
    296. const { data } = await exportInfo[currentTab.value].method(condition)
    297. callback(data, { fileName: `${exportInfo[currentTab.value].prefixName}_${treeNodeData.value.name}` })
    298. }
    299. /**
    300. * 改变分页
    301. */
    302. const changePagination = async ({ paginationInfo }) => {
    303. const { pageNum, pageSize } = paginationInfo
    304. // const { productShowCols, batchShowCols } = tableListRef.value.showCols
    305. await refreshTable.value.fetchResource({
    306. searchInfo: Object.assign(globalSearchInfo.value, {
    307. pageNum,
    308. pageSize
    309. })
    310. })
    311. // const targetColsPropName = currentTab.value === TabsEnum.REGISTER ? 'productShowCols' : 'batchShowCols'
    312. // const allColumns = cloneDeep(globalSearchInfo.value[targetColsPropName])
    313. // await refreshTable.value.fetchResource({
    314. // searchInfo: Object.assign(globalSearchInfo.value, {
    315. // pageNum,
    316. // pageSize,
    317. // productShowCols: deBuildColumns(productShowCols),
    318. // batchShowCols: deBuildColumns(batchShowCols)
    319. // }),
    320. // allColumns
    321. // })
    322. }
    323. /**
    324. * Modal相关
    325. */
    326. //打包主要的视图信息
    327. const buildViewMainInfo = ({ viewInfo }) => {
    328. //保存的视图信息
    329. const { viewName, viewDesc } = viewInfo
    330. //筛选条件
    331. const { productShowCols, batchShowCols } = tableListRef.value.showCols
    332. globalSearchInfo.value.productShowCols = deBuildColumns(productShowCols)
    333. globalSearchInfo.value.batchShowCols = deBuildColumns(batchShowCols)
    334. const condition = globalSearchInfo.value
    335. return { viewName, viewDesc, condition }
    336. }
    337. //保存视图
    338. const saveModalMakeViewHandleOk = async (info, closeSaveModal, extraData) => {
    339. const { viewName, viewDesc, condition } = buildViewMainInfo({ viewInfo: info })
    340. //其他信息
    341. const searchType = SearchTypeEnmu.PRODUCT_INFO
    342. const frameId = extraData?.frameId
    343. let referenceId
    344. if (treeNodeData.value.nodeType === TreeNodeTypeEnum.PRODUCT_FRAM) {
    345. referenceId = frameId || treeNodeData.value.key
    346. }
    347. if (treeNodeData.value.nodeType === TreeNodeTypeEnum.VIEW) {
    348. referenceId = frameId || treeNodeData.value.parentId
    349. }
    350. //传递的总数居
    351. const data = { viewName, viewDesc, condition, searchType, referenceId }
    352. // console.log('保存时,传递的总数居', data)
    353. await saveSearchView(data)
    354. //告知外部,让其刷新树列表
    355. Emiter('saveView', referenceId)
    356. closeSaveModal()
    357. }
    358. //更新视图
    359. const saveModalUpdateViewHandleOk = async (info, closeSaveModal) => {
    360. const { viewName, viewDesc, condition } = buildViewMainInfo({ viewInfo: info })
    361. //其他信息
    362. const searchType = SearchTypeEnmu.PRODUCT_INFO
    363. const frameId = treeNodeData.value.parentId
    364. const id = treeNodeData.value.key
    365. //传递的总数居
    366. const data = { viewName, viewDesc, condition, searchType, id }
    367. // console.log('更新时,传递的总数居', data)
    368. try {
    369. await updateSearchView(data)
    370. } catch (error) {
    371. /**
    372. * 若保存失败,则要回溯对 globalSearchInfo 的更改
    373. * (buildViewMainInfo中,操作了globalSearchInfo)
    374. * */
    375. const { productShowCols, batchShowCols } = tableListRef.value.allCols
    376. globalSearchInfo.value.productShowCols = deBuildColumns(productShowCols)
    377. globalSearchInfo.value.batchShowCols = deBuildColumns(batchShowCols)
    378. }
    379. //告知外部,让其刷新树列表
    380. Emiter('updateView', { frameId, viewName, viewDesc, condition })
    381. closeSaveModal()
    382. }
    383. //删除视图
    384. const deleteModalHandleOk = async (closeDeleteModal) => {
    385. await deleteSearchView(treeNodeData.value.id)
    386. Message.success('删除成功!')
    387. closeDeleteModal()
    388. const frameId = treeNodeData.value.parentId
    389. Emiter('deleteView', frameId)
    390. }
    391. //保存视图权限
    392. const powerModalHandleOk = () => {
    393. //权限相关信息
    394. Emiter('powerModalHandleOk')
    395. }
    396. const frameFilters = ref<FrameFilter[]>([])
    397. const getFrameFilters = async (frameId: string) => {
    398. const res = await getProductFrameFilters(frameId)
    399. frameFilters.value = res.data
    400. }
    401. /**
    402. * 查看余量告警信息
    403. */
    404. // async function handleClickWatchQuantityWarn() {
    405. // const newFilter = Object.assign(globalSearchInfo.value.filter, { alarmOn: SystemYesNoEnum.YES })
    406. // const searchInfo = globalSearchInfo.value
    407. // await refreshAllTabTable(searchInfo)
    408. // }
    409. /**
    410. * 暴露给外部的state
    411. * */
    412. const exposeSaveHandleOpen = computed(() => saveModalRef.value?.saveModalHandleOpen)
    413. const initialTablePresetPagination = computed(() => tableListRef.value?.initialPresetPagination)
    414. const initialTableSelectedRowKeys = computed(() => tableListRef.value?.initialSelectedRowKeys)
    415. defineExpose({
    416. refreshAllTabTable,
    417. getFrameFilters,
    418. saveModalHandleOpen: exposeSaveHandleOpen,
    419. initialTablePresetPagination,
    420. initialTableSelectedRowKeys
    421. })
    422. </script>
    423. <style scoped lang="scss">
    424. .query-view-detail {
    425. background-color: #fff;
    426. main {
    427. padding: 0px 20px 24px;
    428. }
    429. }
    430. .pointer {
    431. cursor: pointer;
    432. }
    433. </style>

    image.png

    1. <template>
    2. <div class="table-list-wrapper">
    3. <div class="tabs-bar">
    4. <div
    5. class="tab-btn pointer"
    6. :class="currentTab.value === Number(type) ? 'tab-active' : ''"
    7. v-for="(item, type) in TabOptions"
    8. :key="type"
    9. @click="changeTab(item, type)"
    10. >
    11. {{ item.title }}({{ item.totalSum }})
    12. </div>
    13. </div>
    14. <div class="table-wrapper">
    15. <BasicTable
    16. ref="tableRef"
    17. rowKey="id"
    18. v-model:columns="TabMapDataSource[currentTab.value].showColumns"
    19. :data="showData"
    20. :isShowFliter="true"
    21. :columnsAll="currentAllCols"
    22. :row-selection="rowSelection"
    23. v-model:selectedRowKeys="selectedRowKeys"
    24. :pagination="pagination"
    25. :scroll="{ y: 600 }"
    26. @select="handleSelected"
    27. @selectAll="handleSelectAll"
    28. column-resizable
    29. :bordered="{ headerCell: true }"
    30. :loading="loading"
    31. >
    32. <template #customTitle>
    33. <a-space>
    34. <span>已选定 {{ selectedRowKeys.length }} 个</span>
    35. <a-button @click="openStockInModal" size="mini">入库</a-button>
    36. <a-button @click="openStockOutModal" size="mini">出库</a-button>
    37. <!-- <a-button size="mini">出库</a-button> -->
    38. </a-space>
    39. </template>
    40. <!-- 序号列 -->
    41. <template #cell-serialNumber="{ item }">
    42. <a-tooltip :mini="true" :position="toolTipPosition" content-class="tool-tip-content">
    43. <template #content>
    44. <span v-for="msg in item.record.alarmMsgs" :key="msg">{{ msg }}</span>
    45. </template>
    46. <icon-exclamation-circle-fill v-if="item.record.alarmOn === 1" :style="{ color: '#ff7d00' }" />
    47. {{ computedShowSerialNumber(item.record.index) }}
    48. </a-tooltip>
    49. </template>
    50. <!-- 产品/批次附加字段 -->
    51. <template #cell-customParams="{ item }">
    52. <a-tooltip :mini="true" :position="toolTipPosition" content-class="tool-tip-content">
    53. <template #content>
    54. <p
    55. v-for="(filed, filedKey) in showAdditionalFields(item.record[item.column.dataIndex], 'array')"
    56. :key="filedKey"
    57. class="tool-tip-content-item"
    58. >
    59. {{ filed }}
    60. </p>
    61. </template>
    62. <a-tag v-if="!isShowEmpty(showAdditionalFields(item.record[item.column.dataIndex], 'string'))">
    63. <span class="pointer">{{ showAdditionalFields(item.record[item.column.dataIndex], 'string') }}</span>
    64. </a-tag>
    65. </a-tooltip>
    66. </template>
    67. <!-- 以 link 形式展示的字段 -->
    68. <template #cell-link="{ item }">
    69. <a-link v-if="item.column.dataIndex === 'batchNum'">
    70. 批次数:
    71. {{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
    72. </a-link>
    73. <a-link v-else>
    74. {{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
    75. </a-link>
    76. </template>
    77. <!-- 以 tag 形式展示的字段 -->
    78. <template #cell-inStockNum="{ item }">
    79. <a-tooltip
    80. :position="toolTipPosition"
    81. :popup-visible="!stockDetialLoading && item.record.id === mouseenterStockInfoCellId"
    82. >
    83. <template #content>
    84. <a-spin :loading="stockDetialLoading" @mouseenter="showStock" @mouseleave="hideStock">
    85. <span class="col-stock-detial-title">库存信息</span>
    86. <p v-for="(str, key) in stockDetialInfoArr" :key="key" class="col-stock-detial-item">{{ str }}</p>
    87. <a-link class="col-stock-detial-item">跳转至库存</a-link>
    88. </a-spin>
    89. </template>
    90. <a-tag class="pointer" @mouseenter="getStockInfo(item.record)" @mouseout="initMouseenterStockInfoCellId">
    91. 在库数量:
    92. {{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
    93. </a-tag>
    94. </a-tooltip>
    95. </template>
    96. <template #cell-default="{ item }">
    97. {{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
    98. </template>
    99. </BasicTable>
    100. </div>
    101. </div>
    102. </template>
    103. <script lang="ts">
    104. type TabMapDataSource = {
    105. [key in TabsEnum]: {
    106. //展示的cols
    107. showColumns: any[]
    108. //编辑的cols信息 (dataIndexArr)
    109. selectShowColumns: string[]
    110. }
    111. }
    112. interface StockDetialInfo {
    113. inStockNum: string | number
    114. inStockAmount: string | number
    115. totalNum: string | number
    116. totalAmount: string | number
    117. }
    118. </script>
    119. <script setup lang="ts">
    120. //api
    121. import { getStockDetialInfo } from '@/api/material'
    122. //components
    123. import BasicTable from '@/components/BasicTable/index.vue'
    124. //utils
    125. import { ref, computed, Ref, watch, toRaw, reactive, nextTick, ShallowRef, shallowRef } from 'vue'
    126. import _, { isEmpty } from 'lodash'
    127. import moment from 'moment'
    128. import { SERIAL_NUMBER } from '@/common/constant'
    129. import { useFetch, useSelectedRows } from '@/hooks'
    130. import { debounce } from '@/util/tools/index'
    131. //types
    132. import { TableRowSelection } from '@arco-design/web-vue'
    133. import { TabsEnum } from '@/views/register/registration-and-batch-info-query/utils'
    134. import { TableColsTypeEnum } from '@/views/register/registration-and-batch-info-query/types'
    135. import { ParameterType } from '@/common/enum'
    136. export type MaterialData = {
    137. id: string
    138. frameId: string
    139. frameName: string
    140. batchName?: string
    141. productName: string
    142. productId: string
    143. batchNum: number
    144. [key: string]: any
    145. }
    146. // const RootData = inject('rootData')
    147. // const { productFrameInfo } = RootData as any
    148. // const getProductFrameName = computed(() => productFrameInfo.value?.name)
    149. const { currentTab, tableListData, loading } = defineProps<{
    150. currentTab: { value: number }
    151. tableListData: any
    152. loading: Ref<boolean>
    153. }>()
    154. const Emiter = defineEmits(['changeTab', 'changePagination', 'openStockInModal', 'openStockOutModal'])
    155. const tableRef = ref()
    156. const rowSelection: TableRowSelection = reactive({
    157. type: 'checkbox',
    158. showCheckedAll: true
    159. })
    160. // tab信息
    161. const TabOptions = computed(() => {
    162. return {
    163. [TabsEnum.REGISTER]: {
    164. title: '注册信息',
    165. totalSum: tableListData[TabsEnum.REGISTER].totalRecords || 0
    166. },
    167. [TabsEnum.BATCH]: {
    168. title: '批次信息',
    169. totalSum: tableListData[TabsEnum.BATCH].totalRecords || 0
    170. }
    171. }
    172. })
    173. //组件内部维护的不同 tab 对应的信息
    174. let TabMapDataSource = reactive<TabMapDataSource>({
    175. [TabsEnum.REGISTER]: {
    176. //展示的cols
    177. showColumns: [],
    178. //编辑的cols信息 (dataIndexArr)
    179. selectShowColumns: []
    180. },
    181. [TabsEnum.BATCH]: {
    182. showColumns: [],
    183. selectShowColumns: []
    184. }
    185. })
    186. const isChangeTabMapDataSource = ref(true)
    187. watch(
    188. TabMapDataSource,
    189. () => {
    190. if (isChangeTabMapDataSource.value) {
    191. if (TabMapDataSource[TabsEnum.REGISTER].showColumns[0]) {
    192. ;(TabMapDataSource[TabsEnum.REGISTER].showColumns[0] as any).customTitle = true
    193. }
    194. isChangeTabMapDataSource.value = false
    195. }
    196. nextTick(() => {
    197. isChangeTabMapDataSource.value = true
    198. })
    199. },
    200. {
    201. immediate: false
    202. }
    203. )
    204. const showData = computed(() => {
    205. return _.isEmpty(TabMapDataSource[currentTab.value].selectShowColumns) ? ([] as []) : (data.value as [])
    206. })
    207. /**
    208. * 索引当前的列表信息
    209. * */
    210. const data = computed<MaterialData[]>(() => {
    211. refreshTableScroll()
    212. return tableListData[currentTab.value].data
    213. })
    214. //当前总的cols
    215. const currentAllCols = computed(() => tableListData[currentTab.value].allColumns)
    216. //map形式的缓存
    217. const currentAllColsToMap = ref({})
    218. //初始化缓存
    219. watch(currentAllCols, (newCurrentCols) => {
    220. currentAllColsToMap.value = {}
    221. newCurrentCols.map((v) => {
    222. currentAllColsToMap.value[v.dataIndex] = v
    223. })
    224. })
    225. //初始化展示的 cols
    226. watch(currentAllCols, () => {
    227. // 所有tab一起初始化,因为如果没有进入某个tab(未编辑),外面在保存时也能拿到该tab所有的列
    228. for (const type in TabMapDataSource) {
    229. if (_.isEmpty(TabMapDataSource[type].showColumns)) {
    230. TabMapDataSource[type].showColumns = tableListData[type].allColumns
    231. }
    232. }
    233. })
    234. /**
    235. * 设置展示列相关
    236. */
    237. //初始化选项(选中全部)
    238. watch(currentAllCols, (newCurrentCols) => {
    239. if (_.isEmpty(TabMapDataSource[currentTab.value].selectShowColumns)) {
    240. TabMapDataSource[currentTab.value].selectShowColumns = newCurrentCols.map((v) => v.dataIndex)
    241. }
    242. })
    243. //(编辑展示列) newSelectShowColumns 就是一个 dataIndexArray
    244. watch(
    245. () => TabMapDataSource[currentTab.value].selectShowColumns,
    246. (newSelectShowColumns) => {
    247. TabMapDataSource[currentTab.value].showColumns = newSelectShowColumns.map((v) => currentAllColsToMap.value[v])
    248. addFixedCol()
    249. }
    250. )
    251. //追加固定列
    252. const addFixedCol = () => {
    253. TabMapDataSource[currentTab.value].showColumns.unshift({
    254. title: '序号',
    255. dataIndex: SERIAL_NUMBER,
    256. customTitle: true,
    257. isShowFliter: false
    258. })
    259. }
    260. /**
    261. * 选择表格数据相关
    262. */
    263. const selectedRowKeys: ShallowRef<string[]> = shallowRef([])
    264. const { selectedRaws: selectedMaterial, handleSelected, handleSelectAll } = useSelectedRows<MaterialData>(data)
    265. const initialSelectedRowKeys = () => {
    266. selectedRowKeys.value = []
    267. selectedMaterial.value = []
    268. tableRef.value.clearSelectedKeysCache()
    269. }
    270. const openStockInModal = () => {
    271. Emiter('openStockInModal', selectedMaterial.value)
    272. }
    273. const openStockOutModal = () => {
    274. Emiter('openStockOutModal', selectedMaterial.value)
    275. }
    276. /**
    277. *
    278. * 切换tab
    279. */
    280. const changeTab = (item, type) => {
    281. initialPresetPagination()
    282. initialSelectedRowKeys()
    283. Emiter('changeTab', { type, paginationInfo })
    284. /**
    285. * TODO
    286. * 发送网络请求、刷新列表(可以在外部做)
    287. */
    288. }
    289. /**
    290. * 分页相关
    291. */
    292. let paginationInfo = reactive({
    293. pageNum: 1,
    294. pageSize: 10
    295. })
    296. const pagination = computed(() => {
    297. const { pageNum, pageSize } = paginationInfo
    298. return {
    299. total: tableListData[currentTab.value].totalRecords,
    300. showPageSize: true,
    301. current: pageNum,
    302. pageSize,
    303. //页码改变时触发
    304. onChange: (current) => {
    305. paginationInfo.pageNum = current
    306. Emiter('changePagination', { paginationInfo })
    307. },
    308. //数据条数改变时触发
    309. onPageSizeChange: (pageSize) => {
    310. paginationInfo.pageNum = 1
    311. paginationInfo.pageSize = pageSize
    312. Emiter('changePagination', { paginationInfo })
    313. }
    314. }
    315. })
    316. const initialPresetPagination = () => {
    317. paginationInfo.pageNum = 1
    318. paginationInfo.pageSize = 10
    319. }
    320. /**
    321. * 附加信息的合理展示
    322. */
    323. function showAdditionalFields(fieldArray, type) {
    324. fieldArray = fieldArray || []
    325. const array = fieldArray.map((field) => {
    326. const name = field.fieldName
    327. const values = customShowByColType(666, field.fieldType, field)
    328. return `${name}:${values}`
    329. })
    330. return type === 'string' ? array.join('、') : array
    331. }
    332. /**
    333. * 根据当前列的字段种类合理展示字段
    334. */
    335. function customShowByColType(type: TableColsTypeEnum, paramType, data) {
    336. //基础列,直接展示
    337. if (type === TableColsTypeEnum.BASIC_BATCH || type === TableColsTypeEnum.BASIC_Product) {
    338. return data
    339. }
    340. /**
    341. * 自定义字段
    342. */
    343. let paramValues = data?.paramValues
    344. paramValues = _.isEmpty(paramValues) ? [] : paramValues
    345. const filterValues = paramValues.map((v) => v.showName)
    346. switch (paramType) {
    347. //时间类型
    348. case ParameterType.MOMENT: {
    349. const showValues = filterValues.map((v) => moment(v).format('YYYY-MM-DD')).join('、')
    350. return showValues
    351. }
    352. default: {
    353. const showValues = filterValues.join('、')
    354. return showValues
    355. }
    356. }
    357. }
    358. const isShowEmpty = (content) => {
    359. return isEmpty(content)
    360. }
    361. /**
    362. * 合理展示序号列
    363. */
    364. function computedShowSerialNumber(num) {
    365. const { pageNum, pageSize } = paginationInfo
    366. const showNum = (pageNum - 1) * pageSize + num
    367. return showNum
    368. }
    369. /**
    370. * 切换 tab 后,更新滚动条位置
    371. */
    372. watch(
    373. () => currentTab.value,
    374. () => {
    375. refreshTableScroll()
    376. }
    377. )
    378. function refreshTableScroll() {
    379. nextTick(() => {
    380. const tableBody = document.querySelector('.arco-table-body')
    381. tableBody?.scroll({ left: 1, behavior: 'smooth' })
    382. })
    383. }
    384. /**
    385. * 判断是否展示 table 自带的 tooltip
    386. */
    387. type Postion = 'left' | 'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br' | 'right' | 'lt' | 'lb' | 'rt' | 'rb' | undefined
    388. const toolTipPosition: Postion = 'left'
    389. // const isShowTableToolTip = (col) => {
    390. // // col.dataIndex !== 'productCustomParams' ? { position: 'bottom' } :false
    391. // return col.dataIndex !== 'productCustomParams' && col.dataIndex !== 'batchCustomParams'
    392. // ? { position: toolTipPosition, mini: true }
    393. // : false
    394. // }
    395. /**
    396. * 鼠标 hover 到库存信息列时 getStockInfo
    397. */
    398. const {
    399. loading: stockDetialLoading,
    400. fetchResource: getStockDetialInfoFetchHook,
    401. result: stockDetialRes
    402. } = useFetch(getStockDetialInfo, {
    403. isLazy: true
    404. })
    405. const stockDetialInfoArr = ref<string[]>()
    406. //记录当前 hover 的库存信息 cell
    407. const mouseenterStockInfoCellId = ref<string>()
    408. const initMouseenterStockInfoCellId = debounce(() => {
    409. if (showFlag == false) {
    410. mouseenterStockInfoCellId.value = ''
    411. }
    412. }, 600)
    413. //获取库位详情信息
    414. const getStockInfo = debounce(async (record) => {
    415. await getStockDetialInfoFetchHook(record.id)
    416. updateStockDetialInfoArr(stockDetialRes.value.data)
    417. mouseenterStockInfoCellId.value = record.id
    418. }, 500)
    419. // 停留展示Stock信息
    420. let showFlag: boolean
    421. const showStock = () => {
    422. showFlag = true
    423. }
    424. const hideStock = () => {
    425. showFlag = false
    426. initMouseenterStockInfoCellId()
    427. }
    428. //构建展示库位详情信息的array
    429. const updateStockDetialInfoArr = (data: StockDetialInfo) => {
    430. const propNameMapPrefix = {
    431. inStockNum: '在库数量:',
    432. inStockAmount: '在库总量:',
    433. totalNum: '入库数量:',
    434. totalAmount: '入库总量:'
    435. }
    436. const detialItems = Object.entries(propNameMapPrefix).map(([key, prefix]) => `${prefix}${data[key] || 0}`)
    437. stockDetialInfoArr.value = detialItems
    438. }
    439. /**
    440. * 暴露给外部的state
    441. */
    442. const initTabMapDataSource = () => {
    443. for (const key in TabMapDataSource) {
    444. TabMapDataSource[key].showColumns = []
    445. TabMapDataSource[key].selectShowColumns = []
    446. }
    447. }
    448. const clearCacheData = () => {
    449. initTabMapDataSource()
    450. }
    451. const showCols = computed(() => {
    452. return {
    453. productShowCols: toRaw(TabMapDataSource[TabsEnum.REGISTER].showColumns),
    454. batchShowCols: toRaw(TabMapDataSource[TabsEnum.BATCH].showColumns)
    455. }
    456. })
    457. const allCols = computed(() => {
    458. return {
    459. productShowCols: toRaw(tableListData[TabsEnum.REGISTER].allColumns),
    460. batchShowCols: toRaw(tableListData[TabsEnum.BATCH].allColumns)
    461. }
    462. })
    463. defineExpose({
    464. selectedRowKeys,
    465. showCols,
    466. allCols,
    467. clearCacheData,
    468. currentAllCols,
    469. //分页相关
    470. paginationInfo,
    471. initialPresetPagination,
    472. //初始化选择项
    473. initialSelectedRowKeys
    474. })
    475. </script>
    476. <style scoped lang="scss">
    477. .table-list-wrapper {
    478. margin-top: 25px;
    479. background-color: #fff;
    480. .tabs-bar {
    481. margin: 16px 0px 8px 0px;
    482. display: flex;
    483. .tab-btn {
    484. width: 142px;
    485. height: 32px;
    486. display: flex;
    487. justify-content: center;
    488. align-items: center;
    489. background-color: #fff;
    490. border-radius: 32px;
    491. }
    492. .tab-active {
    493. color: #3358ff;
    494. background-color: #f2f3f5;
    495. }
    496. }
    497. .table-wrapper {
    498. position: relative;
    499. .editor-show-columns-btn {
    500. position: absolute;
    501. top: 0px;
    502. right: 0px;
    503. z-index: 1;
    504. display: flex;
    505. justify-content: center;
    506. align-items: center;
    507. width: 40px;
    508. height: 40px;
    509. font-size: 15px;
    510. border: 1px solid #e5e6eb;
    511. background-color: #f2f3f5;
    512. }
    513. }
    514. }
    515. .tool-tip-content-item {
    516. margin: 2px 0px;
    517. }
    518. .col-stock-detial-title {
    519. font-size: 15px;
    520. }
    521. .col-stock-detial-item {
    522. font-size: 13px;
    523. }
    524. .pointer {
    525. cursor: pointer;
    526. }
    527. .arco-tag.arco-tag-size-medium.arco-tag-checked {
    528. display: inline-block;
    529. max-width: 100%;
    530. span {
    531. width: 100%;
    532. display: inline-block;
    533. overflow: hidden;
    534. text-overflow: ellipsis;
    535. white-space: nowrap;
    536. }
    537. }
    538. </style>
    539. <style lang="scss">
    540. .tool-tip-content {
    541. display: flex;
    542. flex-direction: column;
    543. }
    544. </style>