<template>
<div class="query-view-detail">
<DetailHeader
@saveModalHandleOpen="saveModalHandleOpen"
@powerModalHandleOpen="powerModalHandleOpen"
@deleteModalHandleOpen="deleteModalHandleOpen"
@handleSelectWarnTypes="handleSelectWarnTypes"
@exportExcel="exportExcel"
/>
<main>
<SearchBar
ref="searchBarRef"
@handleSearch="handleSearch"
@updateFilter="updateFilter"
:filter="filter"
:frameFilters="frameFilters"
:currentTab="currentTab"
/>
<TableList
ref="tableListRef"
:loading="refreshTable.loading"
@changeTab="changeTab" // 改变tab
@changePagination="changePagination"
:currentTab="currentTab" // 有注册信息和批次信息两个tab
:tableListData="tableListData" // 传递的表格数据
@openStockInModal="openStockInModal"
@openStockOutModal="openStockOutModal" // 出库
/>
</main>
<!-- Modals -->
<SaveModal
ref="saveModalRef"
:filter="filter"
@saveModalMakeViewHandleOk="saveModalMakeViewHandleOk"
@saveModalUpdateViewHandleOk="saveModalUpdateViewHandleOk"
/>
<PowerModal ref="powerModalRef" @powerModalHandleOk="powerModalHandleOk" />
<DeleteModal ref="deleteModalRef" @deleteModalHandleOk="deleteModalHandleOk" />
<StockInModal v-model:visible="stockInModalVisible" @refreshCurrentTabTable="refreshCurrentTabTable" />
<!-- 打开出库模态框:在TableList中添加数据点击出库,发送自定义事件给父组件-->
<StockOutModal v-model:visible="stockOutModalVisible" @refreshCurrentTabTable="refreshCurrentTabTable" />
</div>
</template>
<script lang="ts">
export type FrameFilter = {
fieldId: string | null
fieldKey?: string
fieldName: string
fieldType: FilterOriginType
filterType: TableColsTypeEnum
preDefinedValues: string[]
}
</script>
<script setup lang="ts">
//components
import TableList, { MaterialData } from './components/TableList.vue'
import DetailHeader from './components/DetailHeader.vue'
import SaveModal from './components/SaveModal.vue'
import PowerModal from './components/PowerModal.vue'
import DeleteModal from './components/DeleteModal.vue'
import SearchBar from './components/SearchBar.vue'
import { Message } from '@arco-design/web-vue'
// import ResidualQuantityWarn from './components/ResidualQuantityWarn.vue'
import StockInModal from './components/stockInModal/index.vue'
import StockOutModal from './components/stockOutModel/index.vue'
//utils
import { ref, reactive, computed, inject, toRaw, shallowRef, provide } from 'vue'
import {
initSearchInfo,
// 批次还是注册信息的枚举
TabsEnum,
buildColumns,
TreeNodeTypeEnum,
deBuildColumns,
SaveViewModeEnmu
} from '@/views/register/registration-and-batch-info-query/utils'
import { useFetch } from '@/hooks'
//api
import {
// 搜索批次信息的接口
searchBatchInfoList,
// 搜索注册信息的接口
searchProductInfoList,
updateSearchView,
saveSearchView,
deleteSearchView,
getProductFrameFilters,
getExportColumnsData,
getExportProd
} from '@/api/material'
//types
import { Filter, FilterOriginType, SearchInfo, TableColsTypeEnum } from '../types'
import { exportExcelModeEnum, searchSourceEnum } from '@/views/inventory/components/register-search/types'
import { SearchTypeEnmu } from '@/common/enum'
//plugin
import { useI18n } from 'vue-i18n'
import _, { cloneDeep } from 'lodash'
const { t } = useI18n()
const RootData = inject('rootData')
const { treeNodeData } = RootData as any
const Emiter = defineEmits(['saveView', 'updateView', 'deleteView', 'powerModalHandleOk'])
const globalSearchInfo = ref(initSearchInfo())
const setGlobalSearchInfo = (searchInfo) => (globalSearchInfo.value = searchInfo)
const tableListRef = ref()
const stockInModalVisible = ref<boolean>(false)
const stockOutModalVisible = ref<boolean>(false)
const materialDataList = shallowRef<MaterialData[] | undefined>(void 0)
provide('materialDataList', materialDataList)
const openStockInModal = (data: MaterialData[]) => {
stockInModalVisible.value = true
materialDataList.value = data
}
// 打开出库模态框,
const openStockOutModal = (data: MaterialData[]) => {
stockOutModalVisible.value = true
materialDataList.value = data
}
const searchBarRef = ref()
/**
* DetailHeader 相关业务
*/
const saveModalRef = ref()
const powerModalRef = ref()
const deleteModalRef = ref()
const saveModalHandleOpen = () => {
saveModalRef.value.saveModalHandleOpen(SaveViewModeEnmu.CREATE_VIEW, {})
}
const powerModalHandleOpen = () => {
powerModalRef.value.powerModalHandleOpen()
}
const deleteModalHandleOpen = () => {
deleteModalRef.value.deleteModalHandleOpen()
}
const handleSelectWarnTypes = async (selectWarnTypes) => {
const newFilter = Object.assign(globalSearchInfo.value.filter, { alarmTypes: selectWarnTypes })
const searchInfo = globalSearchInfo.value
await refreshAllTabTable({ searchInfo, searchSource: searchSourceEnum.FILTER })
}
/**
* 根据不同的tab,映射不同的接口,获取对应的数据,这里要根据筛选条件去后端搜索
*/
const refreshTabTableMaps = {
// 如果是注册表格
[TabsEnum.REGISTER]: async ({
searchInfo,
searchSource
}: {
searchInfo: SearchInfo
searchSource: searchSourceEnum
}) => {
let ProductTableInfo = await searchProductInfoList(searchInfo)
//拼接展示列的信息
switch (searchSource) {
case searchSourceEnum.SELECT_TREE_NODE: {
ProductTableInfo.allColumns = buildColumns(TabsEnum.REGISTER, searchInfo.productShowCols)
break
}
default: {
ProductTableInfo.allColumns = tableListRef.value.allCols.productShowCols
// ProductTableInfo.showColumns = tableListRef.value.showCols.productShowCols
}
}
// ProductTableInfo.allColumns = buildColumns(TabsEnum.REGISTER, searchInfo.productShowCols)
ProductTableInfo.data = parseCustomParams(ProductTableInfo.data)
tableListData[TabsEnum.REGISTER] = ProductTableInfo
setGlobalSearchInfo(cloneDeep(toRaw(searchInfo)))
filter.value = cloneDeep(toRaw(searchInfo.filter))
},
// 如果是批次信息
[TabsEnum.BATCH]: async ({
searchInfo,
searchSource
}: {
searchInfo: SearchInfo
searchSource: searchSourceEnum
}) => {
let BatchTableInfo = await searchBatchInfoList(searchInfo)
//拼接展示列的信息
switch (searchSource) {
case searchSourceEnum.SELECT_TREE_NODE: {
BatchTableInfo.allColumns = buildColumns(TabsEnum.BATCH, searchInfo.batchShowCols)
break
}
default: {
BatchTableInfo.allColumns = tableListRef.value.allCols.batchShowCols
// BatchTableInfo.showColumns = tableListRef.value.showCols.batchShowCols
}
}
// BatchTableInfo.allColumns = buildColumns(TabsEnum.BATCH, searchInfo.batchShowCols)
BatchTableInfo.data = parseCustomParams(BatchTableInfo.data)
tableListData[TabsEnum.BATCH] = BatchTableInfo
setGlobalSearchInfo(cloneDeep(toRaw(searchInfo)))
filter.value = cloneDeep(toRaw(searchInfo.filter))
}
}
//将一些自定义字段解析到每个 dataItem 的外部
function parseCustomParams(data) {
return data.map((row, index) => {
let batchParams = row?.batchParams || []
let productParams = row?.productParams || []
const params = [...batchParams, ...productParams]
params.map((v) => {
row[v.fieldId] = v
})
//标记序号
row.index = index + 1
return row
})
}
/**
* 刷新所有tab数据
*/
const refreshAllTabTable = async ({
searchInfo,
searchSource
}: {
searchInfo: SearchInfo
searchSource: searchSourceEnum
}) => {
refreshTable.value.loading.value = true
tableListRef.value.clearCacheData() //清除之前表单的缓存数据
await Promise.all(
Object.values(refreshTabTableMaps).map((refreshFunc) => refreshFunc({ searchInfo, searchSource }))
).finally(() => {
refreshTable.value.loading.value = false
})
//回显对应的 keyword
searchBarRef.value.setKeywrod(searchInfo.keyword)
}
/**
* 刷新当前tab数据
*/
const refreshCurrentTabTable = () => {
refreshTabTableMaps[currentTab.value]({ searchInfo: globalSearchInfo.value, searchSource: searchSourceEnum.FILTER })
tableListRef.value.initialSelectedRowKeys()
}
/**
* tab 相关
*/
const currentTab = reactive<{ value: TabsEnum }>({ value: TabsEnum.REGISTER })
// computed返回的是ref,之后访问时需要通过value
const refreshTable = computed(() => {
return useFetch(refreshTabTableMaps[currentTab.value], {
isLazy: true
})
})
// 切换tab事件
const changeTab = async ({ type, paginationInfo }) => {
currentTab.value = Number(type)
const { pageNum, pageSize } = paginationInfo
await refreshTable.value.fetchResource({
searchInfo: Object.assign(globalSearchInfo.value, { pageNum, pageSize }),
searchSource: searchSourceEnum.CHANGE_TAB
})
}
//表单总数
const tableListData = reactive({
[TabsEnum.REGISTER]: {},
[TabsEnum.BATCH]: {}
})
const filter = ref<Filter>({})
/**
* 搜索按钮事件
* TODO
* 根据筛选条件进行搜索
*/
const handleSearch = ({ keyword = '' }: { keyword: string }) => {
// console.log('搜索', keyWord, tableListRef.value.selectedKeys)
//根据不同的节点类型,拿到正确的frameId
let frameId
switch (treeNodeData.value.nodeType) {
case TreeNodeTypeEnum.PRODUCT_FRAM: {
frameId = treeNodeData.value.id
}
case TreeNodeTypeEnum.VIEW: {
frameId = treeNodeData.value.parentId
}
default: {
frameId = treeNodeData.value.id
}
}
//初始化分页数据
tableListRef.value.initialPresetPagination()
const pageNum = tableListRef.value.paginationInfo.pageNum
//刷新列表
const targetParams = { ...globalSearchInfo.value, keyword, pageNum }
refreshTable.value.fetchResource({ searchInfo: targetParams, searchSource: searchSourceEnum.FILTER })
//清空选择列
tableListRef.value.initialSelectedRowKeys()
}
/**
* 筛选条件更新到globalSearchInfo
*/
const updateFilter = (filter: Filter) => {
Object.assign(globalSearchInfo.value.filter, filter)
}
/**
* 导出接口
*/
const exportInfo = {
[TabsEnum.REGISTER]: {
method: getExportProd,
prefixName: t('register.registrationAndBatchInfoQuery.queryViewDetail.product')
},
[TabsEnum.BATCH]: {
method: getExportColumnsData,
prefixName: t('register.registrationAndBatchInfoQuery.queryViewDetail.batch')
}
}
/**
* 导出
*/
const exportExcel = async (mode, callback: Function) => {
let condition = {}
let seletCols: {
productShowCols: any[]
batchShowCols: any[]
} = {
productShowCols: [],
batchShowCols: []
}
switch (mode) {
case exportExcelModeEnum.CURRENT_SHOW: {
const { productShowCols, batchShowCols } = tableListRef.value.showCols
seletCols.productShowCols = deBuildColumns(productShowCols)
seletCols.batchShowCols = deBuildColumns(batchShowCols)
condition = Object.assign({}, globalSearchInfo.value, seletCols)
break
}
default: {
condition = Object.assign({}, globalSearchInfo.value, tableListRef.value.currentAllCols)
break
}
}
const { data } = await exportInfo[currentTab.value].method(condition)
callback(data, { fileName: `${exportInfo[currentTab.value].prefixName}_${treeNodeData.value.name}` })
}
/**
* 改变分页
*/
const changePagination = async ({ paginationInfo }) => {
const { pageNum, pageSize } = paginationInfo
// const { productShowCols, batchShowCols } = tableListRef.value.showCols
await refreshTable.value.fetchResource({
searchInfo: Object.assign(globalSearchInfo.value, {
pageNum,
pageSize
}),
searchSource: searchSourceEnum.FILTER
})
// const targetColsPropName = currentTab.value === TabsEnum.REGISTER ? 'productShowCols' : 'batchShowCols'
// const allColumns = cloneDeep(globalSearchInfo.value[targetColsPropName])
// await refreshTable.value.fetchResource({
// searchInfo: Object.assign(globalSearchInfo.value, {
// pageNum,
// pageSize,
// productShowCols: deBuildColumns(productShowCols),
// batchShowCols: deBuildColumns(batchShowCols)
// }),
// allColumns
// })
}
/**
* Modal相关
*/
//打包主要的视图信息
const buildViewMainInfo = ({ viewInfo }) => {
//保存的视图信息
const { viewName, viewDesc } = viewInfo
//筛选条件
const { productShowCols, batchShowCols } = tableListRef.value.showCols
globalSearchInfo.value.productShowCols = deBuildColumns(productShowCols)
globalSearchInfo.value.batchShowCols = deBuildColumns(batchShowCols)
const condition = globalSearchInfo.value
return { viewName, viewDesc, condition }
}
//保存视图
const saveModalMakeViewHandleOk = async (info, closeSaveModal, extraData) => {
const { viewName, viewDesc, condition } = buildViewMainInfo({ viewInfo: info })
//其他信息
const searchType = SearchTypeEnmu.PRODUCT_INFO
const frameId = extraData?.frameId
let referenceId
if (treeNodeData.value.nodeType === TreeNodeTypeEnum.PRODUCT_FRAM) {
referenceId = frameId || treeNodeData.value.key
}
if (treeNodeData.value.nodeType === TreeNodeTypeEnum.VIEW) {
referenceId = frameId || treeNodeData.value.parentId
}
//传递的总数居
const data = { viewName, viewDesc, condition, searchType, referenceId }
// console.log('保存时,传递的总数居', data)
await saveSearchView(data)
//告知外部,让其刷新树列表
Emiter('saveView', referenceId)
closeSaveModal()
}
//更新视图
const saveModalUpdateViewHandleOk = async (info, closeSaveModal) => {
const { viewName, viewDesc, condition } = buildViewMainInfo({ viewInfo: info })
//其他信息
const searchType = SearchTypeEnmu.PRODUCT_INFO
const frameId = treeNodeData.value.parentId
const id = treeNodeData.value.key
//传递的总数居
const data = { viewName, viewDesc, condition, searchType, id }
// console.log('更新时,传递的总数居', data)
try {
await updateSearchView(data)
} catch (error) {
/**
* 若保存失败,则要回溯对 globalSearchInfo 的更改
* (buildViewMainInfo中,操作了globalSearchInfo)
* */
const { productShowCols, batchShowCols } = tableListRef.value.allCols
globalSearchInfo.value.productShowCols = deBuildColumns(productShowCols)
globalSearchInfo.value.batchShowCols = deBuildColumns(batchShowCols)
}
//告知外部,让其刷新树列表
Emiter('updateView', { frameId, viewName, viewDesc, condition })
closeSaveModal()
}
//删除视图
const deleteModalHandleOk = async (closeDeleteModal) => {
await deleteSearchView(treeNodeData.value.id)
Message.success('删除成功!')
closeDeleteModal()
const frameId = treeNodeData.value.parentId
Emiter('deleteView', frameId)
}
//保存视图权限
const powerModalHandleOk = () => {
//权限相关信息
Emiter('powerModalHandleOk')
}
const frameFilters = ref<FrameFilter[]>([])
const getFrameFilters = async (frameId: string) => {
const res = await getProductFrameFilters(frameId)
frameFilters.value = res.data
}
/**
* 查看余量告警信息
*/
// async function handleClickWatchQuantityWarn() {
// const newFilter = Object.assign(globalSearchInfo.value.filter, { alarmOn: SystemYesNoEnum.YES })
// const searchInfo = globalSearchInfo.value
// await refreshAllTabTable(searchInfo)
// }
/**
* 暴露给外部的state
* */
const exposeSaveHandleOpen = computed(() => saveModalRef.value?.saveModalHandleOpen)
const initialTablePresetPagination = computed(() => tableListRef.value?.initialPresetPagination)
const initialTableSelectedRowKeys = computed(() => tableListRef.value?.initialSelectedRowKeys)
defineExpose({
refreshAllTabTable,
getFrameFilters,
saveModalHandleOpen: exposeSaveHandleOpen,
initialTablePresetPagination,
initialTableSelectedRowKeys
})
</script>
<style scoped lang="scss">
.query-view-detail {
background-color: #fff;
main {
padding: 0px 20px 24px;
}
}
.pointer {
cursor: pointer;
}
</style>
index.vue里面按照筛选条件获取相应的数据,把数据和当前的tab通过props带到TableList里,在TableList里展示选择的tab的数据的数量并通过自定义事件切换Tab。
<!--
* @Author: Ashun
* @Date: 2022-06-20 18:23:06
* @LastEditors: zzd993
* @LastEditTime: 2022-08-12 17:11:07
* @FilePath: \elabnote-front-main\src\views\inventory\components\register-search\query-view-detail\components\TableList.vue
* Copyright (c) 2022 by BMY, All Rights Reserved.
-->
<template>
<div class="table-list-wrapper">
<div class="tabs-bar">
<div
class="tab-btn pointer"
:class="currentTab.value === Number(type) ? 'tab-active' : ''"
v-for="(item, type) in TabOptions"
:key="type"
@click="changeTab(item, type)"
>
{{ item.title }}({{ item.totalSum }})
</div>
</div>
<div class="table-wrapper">
<BasicTable
ref="tableRef"
rowKey="id"
v-model:columns="TabMapDataSource[currentTab.value].showColumns" // 注册或者批次的表格头
:data="showData" // 表格的数据
:isShowFliter="true"
:columnsAll="currentAllCols"
:row-selection="rowSelection"
v-model:selectedRowKeys="selectedRowKeys"
:pagination="pagination"
:scroll="{ y: 600 }"
@select="handleSelected"
@selectAll="handleSelectAll"
column-resizable
:bordered="{ headerCell: true }"
:loading="loading"
>
<template #customTitle>
<a-space>
<span>已选定 {{ selectedRowKeys.length }} 个</span>
<a-button @click="openStockInModal" size="mini">入库</a-button>
<!-- 出库按钮,点击后调用方法触发自定义事件,将打勾选择的数据发送给父组件,父组件通过
provide派发给子组件-->
<a-button @click="openStockOutModal" size="mini">出库</a-button>
</a-space>
</template>
<!-- 序号列 -->
<template #cell-serialNumber="{ item }">
<a-tooltip :mini="true" :position="toolTipPosition" content-class="tool-tip-content">
<template #content>
<span v-for="msg in item.record.alarmMsgs" :key="msg">{{ msg }}</span>
</template>
<icon-exclamation-circle-fill v-if="item.record.alarmOn === 1" :style="{ color: '#ff7d00' }" />
{{ computedShowSerialNumber(item.record.index) }}
</a-tooltip>
</template>
<!-- 产品/批次附加字段 -->
<template #cell-customParams="{ item }">
<a-tooltip :mini="true" :position="toolTipPosition" content-class="tool-tip-content">
<template #content>
<p
v-for="(filed, filedKey) in showAdditionalFields(item.record[item.column.dataIndex], 'array')"
:key="filedKey"
class="tool-tip-content-item"
>
{{ filed }}
</p>
</template>
<a-tag v-if="!isShowEmpty(showAdditionalFields(item.record[item.column.dataIndex], 'string'))">
<span class="pointer">{{ showAdditionalFields(item.record[item.column.dataIndex], 'string') }}</span>
</a-tag>
</a-tooltip>
</template>
<!-- 以 link 形式展示的字段 -->
<template #cell-link="{ item }">
<a-link v-if="item.column.dataIndex === 'batchNum'">
批次数:
{{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
</a-link>
<!--如果是产品tab,点击产品名称就跳转到注册详情页面,-->
<a-link
v-else-if="item.column.dataIndex === 'productName'"
@click="JumpToRegister(item.record[item.column.dataIndex], item.record.id)"
>
{{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
</a-link>
<a-link v-else>
{{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
</a-link>
</template>
<!-- 以 tag 形式展示的字段 -->
<template #cell-inStockNum="{ item }">
<a-tooltip
:position="toolTipPosition"
:popup-visible="!stockDetialLoading && item.record.id === mouseenterStockInfoCellId"
>
<template #content>
<a-spin :loading="stockDetialLoading" @mouseenter="showStock" @mouseleave="hideStock">
<span class="col-stock-detial-title">库存信息</span>
<p v-for="(str, key) in stockDetialInfoArr" :key="key" class="col-stock-detial-item">{{ str }}</p>
<a-link
v-if="!item.record.batchName"
class="col-stock-detial-item"
// 带着产品名称和编号跳转到库存搜索页面并筛选出对应的库存
@click="JumpToStock(item.record.productName, item.record.productPrefix)"
>跳转至库存</a-link
>
<a-link v-else class="col-stock-detial-item"
@click="JumpToStock(item.record.batchName, item.record.productPrefix)"
>跳转至库存</a-link
>
</a-spin>
</template>
<a-tag class="pointer" @mouseenter="getStockInfo(item.record)" @mouseout="initMouseenterStockInfoCellId">
在库数量:
{{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
</a-tag>
</a-tooltip>
</template>
<template #cell-default="{ item }">
{{ customShowByColType(item.column.type, item.column.paramType, item.record[item.column.dataIndex]) }}
</template>
</BasicTable>
</div>
</div>
</template>
<script lang="ts">
// 根据tab的不同切换展示的表头列信息
type TabMapDataSource = {
[key in TabsEnum]: {
//展示的cols
showColumns: any[]
//编辑的cols信息 (dataIndexArr)
selectShowColumns: string[]
}
}
interface StockDetialInfo {
inStockNum: string | number
inStockAmount: string | number
totalNum: string | number
totalAmount: string | number
}
</script>
<script setup lang="ts">
//api
import { getStockDetialInfo } from '@/api/material'
//components
import BasicTable from '@/components/BasicTable/index.vue'
//utils
import { ref, computed, Ref, watch, toRaw, reactive, nextTick, ShallowRef, shallowRef } from 'vue'
import _, { isEmpty } from 'lodash'
import moment from 'moment'
import { SERIAL_NUMBER } from '@/common/constant'
import { useFetch, useSelectedRows } from '@/hooks'
import { debounce } from '@/util/tools/index'
//types
import { TableRowSelection } from '@arco-design/web-vue'
import { TabsEnum } from '@/views/register/registration-and-batch-info-query/utils'
import { TableColsTypeEnum } from '@/views/register/registration-and-batch-info-query/types'
import { ParameterType } from '@/common/enum'
import { useTagViewStore } from '@/store/modules/tag-view'
import { ComponentTypeEnum } from '@/common/enum'
import { useRouter } from 'vue-router'
import { getViewDataMapCipher } from '@/api/material'
export type MaterialData = {
id: string
frameId: string
frameName: string
batchName?: string
productName: string
productId: string
batchNum: number
[key: string]: any
}
const store = useTagViewStore()
const router = useRouter()
// const RootData = inject('rootData')
// const { productFrameInfo } = RootData as any
// const getProductFrameName = computed(() => productFrameInfo.value?.name)
const { currentTab, tableListData, loading } = defineProps<{
// 当前的tab和表格数据
currentTab: { value: number }
tableListData: any
loading: Ref<boolean>
}>()
const Emiter = defineEmits(['changeTab', 'changePagination', 'openStockInModal', 'openStockOutModal'])
const tableRef = ref()
const rowSelection: TableRowSelection = reactive({
type: 'checkbox',
showCheckedAll: true
})
// tab信息
const TabOptions = computed(() => {
return {
[TabsEnum.REGISTER]: {
title: '注册信息',
totalSum: tableListData[TabsEnum.REGISTER].totalRecords || 0
},
[TabsEnum.BATCH]: {
title: '批次信息',
totalSum: tableListData[TabsEnum.BATCH].totalRecords || 0
}
}
})
//组件内部维护的不同 tab 对应的信息
let TabMapDataSource = reactive<TabMapDataSource>({
[TabsEnum.REGISTER]: {
//展示的cols
showColumns: [],
//编辑的cols信息 (dataIndexArr)
selectShowColumns: []
},
[TabsEnum.BATCH]: {
showColumns: [],
selectShowColumns: []
}
})
const isChangeTabMapDataSource = ref(true)
watch(
TabMapDataSource,
() => {
if (isChangeTabMapDataSource.value) {
if (TabMapDataSource[TabsEnum.REGISTER].showColumns?.[0]) {
;(TabMapDataSource[TabsEnum.REGISTER].showColumns[0] as any).customTitle = true
}
isChangeTabMapDataSource.value = false
}
nextTick(() => {
isChangeTabMapDataSource.value = true
})
},
{
immediate: false
}
)
const showData = computed(() => {
return _.isEmpty(TabMapDataSource[currentTab.value].selectShowColumns) ? ([] as []) : (data.value as [])
})
/**
* 索引当前的列表信息
* */
const data = computed<MaterialData[]>(() => {
refreshTableScroll()
return tableListData[currentTab.value].data
})
//当前总的cols
const currentAllCols = computed(() => tableListData[currentTab.value].allColumns)
// const currentAllCols = ref<any[]>([])
// watch(
// () => tableListData[currentTab.value].allColumns,
// (newAllColumns) => {
// console.log(newAllColumns)
// currentAllCols.value = newAllColumns || []
// }
// )
//map形式的缓存
const currentAllColsToMap = ref({})
//初始化缓存
watch(currentAllCols, (newCurrentCols) => {
currentAllColsToMap.value = {}
newCurrentCols?.map?.((v) => {
currentAllColsToMap.value[v.dataIndex] = v
})
})
//初始化展示的 cols
watch(currentAllCols, () => {
// 所有tab一起初始化,因为如果没有进入某个tab(未编辑),外面在保存时也能拿到该tab所有的列
for (const type in TabMapDataSource) {
if (_.isEmpty(TabMapDataSource[type].showColumns)) {
TabMapDataSource[type].showColumns = tableListData[type].allColumns
// console.log(tableListData[type].showColumns)
}
}
})
/**
* 设置展示列相关
*/
//初始化选项(选中全部)
watch(currentAllCols, (newCurrentCols) => {
if (_.isEmpty(TabMapDataSource[currentTab.value].selectShowColumns)) {
const targetShowCols = tableListData[currentTab.value].showColumns || newCurrentCols
TabMapDataSource[currentTab.value].selectShowColumns = targetShowCols.map((v) => v.dataIndex)
// console.log(TabMapDataSource[currentTab.value].selectShowColumns )
}
})
//(编辑展示列) newSelectShowColumns 就是一个 dataIndexArray
watch(
() => TabMapDataSource[currentTab.value].selectShowColumns,
(newSelectShowColumns) => {
TabMapDataSource[currentTab.value].showColumns = newSelectShowColumns.map((v) => currentAllColsToMap.value[v])
addFixedCol()
}
)
//追加固定列
const addFixedCol = () => {
TabMapDataSource[currentTab.value].showColumns.unshift({
title: '序号',
dataIndex: SERIAL_NUMBER,
customTitle: true,
isShowFliter: false
})
}
/**
* 选择表格数据相关
*/
const selectedRowKeys: ShallowRef<string[]> = shallowRef([])
const { selectedRaws: selectedMaterial, handleSelected, handleSelectAll } = useSelectedRows<MaterialData>(data)
const initialSelectedRowKeys = () => {
selectedRowKeys.value = []
selectedMaterial.value = []
tableRef.value.clearSelectedKeysCache()
}
const openStockInModal = () => {
Emiter('openStockInModal', selectedMaterial.value)
}
const openStockOutModal = () => {
// 通过发送自定义事件并将选中的数据发送给父组件index.vue,父组件执行自定义事件打开模态框并通过provide暴露给StockOutModal组件,它用inject接收展示选中的数据
Emiter('openStockOutModal', selectedMaterial.value)
}
/**
* 切换tab
*/
const changeTab = (item, type) => {
initialPresetPagination()
initialSelectedRowKeys()
Emiter('changeTab', { type, paginationInfo })
/**
* TODO
* 发送网络请求、刷新列表(可以在外部做)
*/
}
/**
* 分页相关
*/
let paginationInfo = reactive({
pageNum: 1,
pageSize: 10
})
const pagination = computed(() => {
const { pageNum, pageSize } = paginationInfo
return {
total: tableListData[currentTab.value].totalRecords,
showPageSize: true,
current: pageNum,
pageSize,
//页码改变时触发
onChange: (current) => {
paginationInfo.pageNum = current
Emiter('changePagination', { paginationInfo })
},
//数据条数改变时触发
onPageSizeChange: (pageSize) => {
paginationInfo.pageNum = 1
paginationInfo.pageSize = pageSize
Emiter('changePagination', { paginationInfo })
}
}
})
const initialPresetPagination = () => {
paginationInfo.pageNum = 1
paginationInfo.pageSize = 10
}
/**
* 附加信息的合理展示
*/
function showAdditionalFields(fieldArray, type) {
fieldArray = fieldArray || []
const array = fieldArray.map((field) => {
const name = field.fieldName
const values = customShowByColType(666, field.fieldType, field)
return `${name}:${values}`
})
return type === 'string' ? array.join('、') : array
}
/**
* 根据当前列的字段种类合理展示字段
*/
function customShowByColType(type: TableColsTypeEnum, paramType, data) {
//基础列,直接展示
if (type === TableColsTypeEnum.BASIC_BATCH || type === TableColsTypeEnum.BASIC_Product) {
return data
}
/**
* 自定义字段
*/
let paramValues = data?.paramValues
paramValues = _.isEmpty(paramValues) ? [] : paramValues
const filterValues = paramValues.map((v) => v.showName)
switch (paramType) {
//时间类型
case ParameterType.MOMENT: {
const showValues = filterValues.map((v) => moment(v).format('YYYY-MM-DD')).join('、')
return showValues
}
default: {
const showValues = filterValues.join('、')
return showValues
}
}
}
const isShowEmpty = (content) => {
return isEmpty(content)
}
/**
* 合理展示序号列
*/
function computedShowSerialNumber(num) {
const { pageNum, pageSize } = paginationInfo
const showNum = (pageNum - 1) * pageSize + num
return showNum
}
/**
* 切换 tab 后,更新滚动条位置
*/
watch(
() => currentTab.value,
() => {
refreshTableScroll()
}
)
function refreshTableScroll() {
nextTick(() => {
const tableBody = document.querySelector('.arco-table-body')
tableBody?.scroll({ left: 1, behavior: 'smooth' })
})
}
/**
* 判断是否展示 table 自带的 tooltip
*/
type Postion = 'left' | 'top' | 'tl' | 'tr' | 'bottom' | 'bl' | 'br' | 'right' | 'lt' | 'lb' | 'rt' | 'rb' | undefined
const toolTipPosition: Postion = 'left'
// const isShowTableToolTip = (col) => {
// // col.dataIndex !== 'productCustomParams' ? { position: 'bottom' } :false
// return col.dataIndex !== 'productCustomParams' && col.dataIndex !== 'batchCustomParams'
// ? { position: toolTipPosition, mini: true }
// : false
// }
/**
* 鼠标 hover 到库存信息列时 getStockInfo
*/
const {
loading: stockDetialLoading,
fetchResource: getStockDetialInfoFetchHook,
result: stockDetialRes
} = useFetch(getStockDetialInfo, { // 这个函数封装了发送请求的逻辑,在请求接口需要用到 loading 状态的时候,直接用内部返回的 loading 状态,
// 内部处理了重复调用接口 loading 状态失效的问题
isLazy: true
})
const stockDetialInfoArr = ref<string[]>()
//记录当前 hover 的库存信息 cell
const mouseenterStockInfoCellId = ref<string>()
const initMouseenterStockInfoCellId = debounce(() => {
if (showFlag == false) {
mouseenterStockInfoCellId.value = ''
}
}, 600)
//获取库位详情信息
const getStockInfo = debounce(async (record) => {
await getStockDetialInfoFetchHook(record.id)
updateStockDetialInfoArr(stockDetialRes.value.data)
mouseenterStockInfoCellId.value = record.id
}, 500)
// 停留展示Stock信息
let showFlag: boolean
const showStock = () => {
showFlag = true
}
const hideStock = () => {
showFlag = false
initMouseenterStockInfoCellId()
}
//构建展示库位详情信息的array
const updateStockDetialInfoArr = (data: StockDetialInfo) => {
const propNameMapPrefix = {
inStockNum: '在库数量:',
inStockAmount: '在库总量:',
totalNum: '入库数量:',
totalAmount: '入库总量:'
}
const detialItems = Object.entries(propNameMapPrefix).map(([key, prefix]) => `${prefix}${data[key] || 0}`)
stockDetialInfoArr.value = detialItems
}
/**
* 暴露给外部的state
*/
const initTabMapDataSource = () => {
for (const key in TabMapDataSource) {
TabMapDataSource[key].showColumns = []
TabMapDataSource[key].selectShowColumns = []
}
}
const clearCacheData = () => {
initTabMapDataSource()
}
const showCols = computed(() => {
return {
productShowCols: toRaw(TabMapDataSource[TabsEnum.REGISTER].showColumns),
batchShowCols: toRaw(TabMapDataSource[TabsEnum.BATCH].showColumns)
}
})
const allCols = computed(() => {
return {
productShowCols: toRaw(tableListData[TabsEnum.REGISTER].allColumns),
batchShowCols: toRaw(tableListData[TabsEnum.BATCH].allColumns)
}
})
// 跳转到注册详情页
const JumpToRegister = (title: string, key: string) => {
// console.log(title)
// console.log(key)
store.changeTagView({
title: title,
key: key,
type: ComponentTypeEnum.PRODUCT_DETIAL
})
router.push('/main/register')
}
// 跳转到库存
const JumpToStock = async (names: string) => {
const { data } = await getViewDataMapCipher({
currentNodeType: 'STOCK_STATUS',
searchInfo: {
showCols: [],
pageNum: 1,
pageSize: 10,
keyword: '',
filter: { productNames: [names], perception: 10, perfixes: [productPerfix] }
},
stockStatusNodeData: {
id: 10,
name: '在库',
nodeType: 'STOCK_STATUS'
},
viewNodeData: {}
})
// console.log(names)
// console.log(data)
router.push(`/main/_inventory/search/${data}`)
}
defineExpose({
selectedRowKeys,
showCols,
allCols,
clearCacheData,
currentAllCols,
//分页相关
paginationInfo,
initialPresetPagination,
//初始化选择项
initialSelectedRowKeys
})
</script>
<style scoped lang="scss">
.table-list-wrapper {
margin-top: 25px;
background-color: #fff;
.tabs-bar {
margin: 16px 0px 8px 0px;
display: flex;
.tab-btn {
width: 142px;
height: 32px;
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
border-radius: 32px;
}
.tab-active {
color: #3358ff;
background-color: #f2f3f5;
}
}
.table-wrapper {
position: relative;
.editor-show-columns-btn {
position: absolute;
top: 0px;
right: 0px;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
font-size: 15px;
border: 1px solid #e5e6eb;
background-color: #f2f3f5;
}
}
}
.tool-tip-content-item {
margin: 2px 0px;
}
.col-stock-detial-title {
font-size: 15px;
}
.col-stock-detial-item {
font-size: 13px;
}
.pointer {
cursor: pointer;
}
.arco-tag.arco-tag-size-medium.arco-tag-checked {
display: inline-block;
max-width: 100%;
span {
width: 100%;
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
</style>
<style lang="scss">
.tool-tip-content {
display: flex;
flex-direction: column;
}
</style>
封装的useFetch。
import { Ref, ref } from 'vue'
interface IFetchResult {
loading: Ref<boolean>
error: Ref<any>
result: Ref<any>
fetchResource: (...args: any) => {}
}
interface IOptions<T> {
params?: T
isLazy?: boolean
}
/**
* 包装请求接口的 hook,内部处理了 loading 状态,
* 在请求接口需要用到 loading 状态的时候,直接用内部返回的 loading 状态,
* 内部处理了重复调用接口 loading 状态失效的问题
* @param {Function} api 需要请求的接口
* @param {{ params: any, isLazy: boolean }} options 配置项 params: 参数信息, isLazy: 是否惰性触发(默认不惰性触发)
* @returns {Object}
*/
export const useFetch = <T, P>(
api: Function,
options: IOptions<T> = { params: undefined, isLazy: false }
): IFetchResult => {
const loading = ref(false)
const result = ref<P | null>(null)
const error = ref(null)
const { params, isLazy } = options
const fetchResource = async (params) => {
loading.value = true
const data = await api(params)
.then((res) => {
loading.value = false
result.value = res
})
.catch((e) => {
if (e !== 'ERR_CANCELED') {
loading.value = false
}
error.value = e
})
return data
}
!isLazy && fetchResource(params)
return {
loading,
error,
result,
fetchResource
}
}
我写的出库页面,有权限的数据可以展示库存信息,直接跳转至库存;没有权限的数据需要申请出库,再打开个模态框,跳转到申请出库。通过watch监视父组件传过来的materialDataList并取出需要的字段拼成tableListData。再用watch监视tableListData,把它分成有权限的数组和没有权限的数组,当tableListData数据有变化时,对每个数据,取出后端接口需要的信息,统一将他们放到一个数组里,然后向后端发送请求,得到每个数据是否有权限的数组,再遍历变化的数据,为其添加一条hasStockPermission属性,判断该数据的hasStockPermission属性,将其加入不同的数组。
<!--
* @Author: zzd993
* @Date: 2022-08-11 11:46:14
* @LastEditors: zzd993
* @LastEditTime: 2022-08-22 16:02:59
* @FilePath: \elabnote-front-main\src\views\inventory\components\register-search\query-view-detail\components\stockOutModel\index.vue
* Copyright (c) 2022 by BMY, All Rights Reserved.
-->
<template>
<a-modal :width="900" :visible="visible" title-align="start" title="出库" @cancel="handleClose">
<div class="warn">
全部{{ tableListData?.length }}项,其中{{
applyArr.length
}}项需向所属人或负责人<span>申请出库</span>出库,其余支持跳转至库存出库
</div>
<!-- 此处使用inject接收的materialDataList然后将其取出需要的字段拼成tableListData-->
<BasicTable :columns="materialTableConfig" :data="tableListData" :pagination="false">
<template #cell-productName="{ item }">
<ALink>
{{ item.record[item.column.dataIndex] }}
</ALink>
</template>
<!-- 以 tag 形式展示的字段 -->
<template #cell-inStockNum="{ item }">
<template v-if="item.record.hasStockPermission">
<!-- <a-tag @click="showInfo(item)"> -->
<ATag @click="showInfo(item)">
在库数量:
{{ item.record[item.column.dataIndex] }}
</ATag>
</template>
<template v-else>
<SvgIcon iconName="warning1" iconSize="24" />
</template>
</template>
</BasicTable>
<!--出库申请的模态框,传递props是需要申请出库的数据,子组件用emits通过父组件更新数据-->
<ApplyStockOut ref="ApplyStockOutRef" v-model:applyArr="applyArr" @clear="clear" />
<!--arco的插槽,允许自定义页脚-->
<template #footer>
<a-button @click="JumpStock">跳转至库存</a-button>
<a-button type="primary" @click="handleOk">申请出库</a-button>
</template>
</a-modal>
</template>
<script lang="ts">
type MaterialTableListItem = {
productPrefix: string
productName: string
batchName: string | undefined
id: string
batchNum: number
inStockNum: number
owner: string | undefined
[key: string]: any
}
</script>
<script setup lang="ts">
import _ from 'lodash'
import BasicTable from '@/components/BasicTable/index.vue'
import ApplyStockOut from './ApplyStockOut.vue'
import { inject, ShallowRef, ref, watchEffect, watch, reactive } from 'vue'
import { MaterialTypeEnum } from '@/common/enum'
//types
import type { MaterialData } from '../TableList.vue'
// import type { TableData } from '@arco-design/web-vue'
//utils
//api
import { getViewDataMapCipher } from '@/api/material'
import { checkProductPermission } from '@/api/material'
import { ReferenceList } from '@/api/material'
//store
import { materialTableConfig } from './tableConfig'
import { useRouter } from 'vue-router'
const router = useRouter()
// 表格数据
const materialDataList: ShallowRef<MaterialData[]> | undefined = inject('materialDataList')
const tableListData = ref<MaterialTableListItem[] | undefined>(void 0)
const visible = ref<boolean>(false)
const emits = defineEmits(['update:visible', 'refreshCurrentTabTable'])
// 跳转至库存的数组
const jumpArr = reactive([] as any)
// 申请出库的数组
const applyArr = reactive([] as any)
const showInfo = (item) => {
// console.log(item)
}
// 请求的权限列表
const referenceList: ReferenceList[] = reactive([])
watch(tableListData, async (newVal) => {
jumpArr.length = 0
applyArr.length = 0
referenceList.length = 0
newVal?.forEach((item) => {
if (!item.batchName) {
item.referenceType = MaterialTypeEnum.PRODUCT
} else {
item.referenceType = MaterialTypeEnum.PRODUCT_BATCH
}
let referenceitem = {
referenceType: item.referenceType,
referenceId: item.id
}
referenceList.push(referenceitem)
})
const { data } = await checkProductPermission({ referenceList: referenceList })
newVal?.forEach((item, index) => {
item.hasStockPermission = data[index].hasStockPermission
if (item.hasStockPermission) {
jumpArr.push(item)
// applyArr.push(item)
} else {
applyArr.push(item)
}
})
// console.log('newVal', newVal)
})
//
const clear = () => {
if (tableListData.value) {
// let newArr = tableListData.value.filter((item) => !applyArr.some((i) => item.id === i.id))
// console.log(newArr)
// console.log(jumpArr)
tableListData.value = _.cloneDeep(jumpArr)
// console.log(tableListData.value)
}
}
// 监听materialDataList取出数据放到tableListData
watchEffect(() => {
if (materialDataList?.value) {
// console.log('materialDataList', materialDataList.value)
tableListData.value = materialDataList.value.map((item) => {
const { id, productPrefix, owner, productName, batchName, batchNum, inStockNum, frameName, productId } = item
return {
id,
productPrefix,
owner,
productName,
batchName,
batchNum,
inStockNum,
frameName,
productId
}
})
// console.log(tableListData.value)
}
})
// 跳转到库存
const JumpToStock = async () => {
const productNamesArr = [] as any
const prefixArr = [] as any
jumpArr.forEach((item) => {
productNamesArr.push(item.productName)
prefixArr.push(item.productPrefix)
})
const { data } = await getViewDataMapCipher({
currentNodeType: 'STOCK_STATUS',
searchInfo: {
showCols: [],
pageNum: 1,
pageSize: 10,
keyword: '',
filter: { productNames: productNamesArr, perception: 10, perfixes: prefixArr }
},
stockStatusNodeData: {
id: 10,
name: '在库',
nodeType: 'STOCK_STATUS'
},
viewNodeData: {}
})
router.push(`/main/_inventory/search/${data}`)
}
const ApplyStockOutRef = ref<InstanceType<typeof ApplyStockOut>>()
// 取消
const handleClose = () => {
emits('update:visible', false)
}
// 跳转到库存
const JumpStock = () => {
emits('update:visible', false)
JumpToStock()
}
/**
* 确定,打开申请出库模态框
*/
const handleOk = () => {
ApplyStockOutRef.value!.openModal()
}
</script>
<style scoped lang="scss">
:deep(.arco-form-item-label) {
color: $font3Color;
font-size: 14px;
margin-top: 8px;
}
:deep(.arco-form-item) {
margin-bottom: 0;
}
.warn {
width: 858px;
height: 40px;
text-align: center;
line-height: 40px;
background: rgb(250, 155, 67);
margin-bottom: 10px;
span {
color: blue;
}
}
</style>
/*
* @Author: zzd993
* @Date: 2022-08-12 18:04:02
* @LastEditors: zzd993
* @LastEditTime: 2022-08-22 16:03:52
* @FilePath: \elabnote-front-main\src\views\inventory\components\register-search\query-view-detail\components\stockOutModel\tableConfig.ts
* Copyright (c) 2022 by BMY, All Rights Reserved.
*/
import { TableColumnData } from '@arco-design/web-vue/es/table/interface'
export const materialTableConfig: TableColumnData[] = [
{
title: '样本编号',
dataIndex: 'productPrefix',
slotName: 'cell-productPrefix'
},
{
title: '样本名称',
dataIndex: 'productName',
slotName: 'cell-productName'
},
{
title: '批次号',
dataIndex: 'batchName',
slotName: 'cell-batchName'
},
{
title: '库存信息',
dataIndex: 'inStockNum',
slotName: 'cell-inStockNum'
},
{
title: '所属人',
dataIndex: 'owner'
// slotName: 'cell-owner'
}
]
<template>
<a-modal
:width="900"
:visible="visible"
title-align="start"
title="申请出库"
@cancel="handleCancel"
@ok="handleOk"
okText="确定"
unmount-on-close
>
<BasicTable :columns="columns" :data="applyArr" :pagination="false">
<template #productName="{ item }">
<a-link> {{ item.record.productName }} </a-link>
</template>
<!-- 申请量 -->
<template #total="{ item }">
<a-input-number :min="0" type="text" v-model="item.record.approveNum" size="mini" />
</template>
<!-- 量单位 -->
<template #unitName="{ item }">
<a-select
:options="unitsOptions"
:field-names="{ label: 'name', value: 'id' }"
size="mini"
default-value=""
// id是每次变化时拿到的单位的id,这里不能破坏arco组件,所以想要再传递item信息需要返回一个函数
@change="(id) => onChange(id, item)"
/>
</template>
<!-- 备注 -->
<template #remarks="{ item }">
<a-input type="text" v-model="item.record.remarks" placeholder="请填写备注" />
</template>
</BasicTable>
<a-form ref="FormRef" :model="form" :style="{ width: '300px' }" layout="vertical">
<a-form-item field="recallLocale" label="请选择出库地点">
<a-select v-model="form.recallLocale" style="width: 100%">
<a-option
v-for="(label, localeItem) of recallLocaleOptions"
:key="localeItem"
:value="localeItem"
:label="label"
/>
</a-select>
</a-form-item>
<div class="recall-detial" v-if="form.recallLocale == RecallLocale.IN_PRIVATE">
<SelectOutStockProject ref="SelectOutStockProjectRef" />
<a-form-item field="recallDate" label="如果需要返库,请选择返库时间">
<a-date-picker v-model="form.recallDate" style="width: 100%" />
</a-form-item>
</div>
</a-form>
</a-modal>
</template>
<script lang="ts">
import { RecallLocale } from '@/api/material'
import { isEmpty } from 'lodash'
const recallLocaleOptions = Object.freeze({
[RecallLocale.IN_PRIVATE]: '研发',
[RecallLocale.GUGANGZHOU_WITHDRAWAL]: '广州&退仓'
})
interface FormInfo {
recallLocale: RecallLocale
recallDate: Date | string
projectIds: string[]
}
</script>
<script setup lang="ts">
//components
import SelectOutStockProject from '@/views/inventory/components/search/query-view-detail/components/SelectOutStockProject.vue'
//api
import { outApprove } from '@/api/material'
//utils
import { computed, reactive, ref } from 'vue'
//type
import type { TableColumnData, Form } from '@arco-design/web-vue'
import { IoutApproveData } from '@/api/material'
import { useUserStore } from '@/store/modules/user'
const visible = ref<boolean>(false)
interface ItableListData {
productPrefix: string
productName: string
batchName: string | undefined
id: string
batchNum: number
inStockNum: number
owner: string | undefined
[x: string]: any
}
const props = defineProps<{
applyArr: ItableListData[] | undefined
}>()
const onChange = (id, item) => {
unitsOptions.forEach((i) => {
if (i.id === id) {
item.record.unitId = i.id
item.record.unitName = i.name
}
})
// console.log(item)
}
const emits = defineEmits(['update: applyArr', 'clear'])
const FormRef = ref<InstanceType<typeof Form> | null>(null)
const form = reactive<FormInfo>({
recallLocale: RecallLocale.IN_PRIVATE,
recallDate: undefined as unknown as Date,
projectIds: []
})
// 初始化表单
const initFormInfo = () => {
// 出库地点
form.recallLocale = RecallLocale.IN_PRIVATE
// 返库日期
form.recallDate = ''
// 项目id
form.projectIds = []
}
const userStore = useUserStore()
// 在store中取出单位
const unitsOptions = userStore.volumeUnits.map((unit) => ({ id: unit.id, name: unit.unitName }))
const STOCK_COLUMNS: Array<TableColumnData> = [
{
title: '序号',
width: 80,
render: ({ rowIndex }) => rowIndex + 1
},
{
title: '样本编号',
dataIndex: 'productPrefix',
width: 80,
ellipsis: true,
tooltip: true
},
{
title: '样本名称',
dataIndex: 'productName',
slotName: 'productName',
width: 80,
ellipsis: true,
tooltip: true
},
{
title: '批次号',
dataIndex: 'batchName',
slotName: 'batchName',
width: 80,
ellipsis: true,
tooltip: true
},
{
title: '申请量',
dataIndex: 'total',
slotName: 'total',
width: 80,
ellipsis: true,
tooltip: true
},
{
title: '量单位',
dataIndex: 'unitName',
slotName: 'unitName',
width: 80,
ellipsis: true,
tooltip: true
},
{
title: '备注',
dataIndex: 'remarks',
slotName: 'remarks',
width: 120,
ellipsis: true,
tooltip: true
}
]
const columns = computed(() => {
return STOCK_COLUMNS
})
const handleCancel = () => {
visible.value = false
}
/**
* 确定
*/
const SelectOutStockProjectRef = ref()
function handleConfirmCancelModal() {
handleCancel()
}
async function handleOutStock(outApproveData: IoutApproveData) {
await outApprove(outApproveData)
if (props.applyArr) {
props.applyArr.length = 0
}
emits('clear')
handleConfirmCancelModal()
}
function confirmOperate() {
FormRef.value!.validate(async (error) => {
if (!isEmpty(error)) return
let productOrBatchStockOutList
const directStockOut = form.recallLocale
let outStockData = { directStockOut: directStockOut }
if (directStockOut === RecallLocale.IN_PRIVATE) {
if (props.applyArr) {
// 拼接要给后端的数据
productOrBatchStockOutList = props.applyArr.map((item) => ({
productId: item.productId as string,
productNo: item.productPrefix as string,
productName: item.productName as string,
batchId: item.id,
batchNo: item.batchName,
approveNum: item.approveNum,
measuringUnitId: item.unitId,
measuringUnitName: item.unitName,
remarks: item.remarks as string
}))
const { isValidate, projectIds } = await SelectOutStockProjectRef.value.validate()
if (!isValidate) return
form.projectIds = projectIds
Object.assign(outStockData, {
productOrBatchStockOutList,
projectIds: form.projectIds,
recallDate: form.recallDate as unknown as string,
isLocked: 2
})
await handleOutStock(outStockData)
}
} else {
if (props.applyArr) {
productOrBatchStockOutList = props.applyArr.map((item) => ({
productId: item.productId as string,
productNo: item.productPrefix as string,
productName: item.productName as string,
batchId: item.id,
batchNo: item.batchName,
approveNum: item.approveNum,
measuringUnitId: item.unitId,
measuringUnitName: item.unitName,
remarks: item.remarks as string
}))
Object.assign(outStockData, {
productOrBatchStockOutList,
projectIds: null,
recallDate: null,
isLocked: 2
})
await handleOutStock(outStockData)
}
}
})
return true
}
const handleOk = async () => {
confirmOperate()
}
defineExpose({
// 打开模态框初始化表单,并给props的每个数据加入属性:申请量、单位id、单位名称、备注
openModal: () => {
initFormInfo()
if (props && props.applyArr) {
props.applyArr.map((item) => ({
...item,
approveNum: 0,
unitId: '',
unitName: '',
remarks: ''
}))
}
visible.value = true
}
})
</script>
<style scoped lang="scss">
:deep(.arco-form-item-label) {
color: $font3Color;
font-size: 14px;
margin-top: 8px;
}
:deep(.arco-form-item) {
margin-bottom: 0;
}
</style>