466 lines
14 KiB
Vue
466 lines
14 KiB
Vue
<template>
|
|
<div class="page-container">
|
|
<n-card>
|
|
<!-- 搜索表单 -->
|
|
<div class="search-form">
|
|
<n-form inline :model="searchForm" label-placement="left">
|
|
<n-form-item>
|
|
<n-space>
|
|
<n-button type="primary" @click="handleSearch">
|
|
<template #icon><n-icon><SearchOutline /></n-icon></template>
|
|
搜索
|
|
</n-button>
|
|
<n-button @click="handleReset">
|
|
<template #icon><n-icon><RefreshOutline /></n-icon></template>
|
|
重置
|
|
</n-button>
|
|
</n-space>
|
|
</n-form-item>
|
|
</n-form>
|
|
</div>
|
|
|
|
<!-- 工具栏 -->
|
|
<!-- <div class="table-toolbar">
|
|
<n-space>
|
|
<n-button type="primary" @click="handleAdd">
|
|
<template #icon><n-icon><AddOutline /></n-icon></template>
|
|
新增
|
|
</n-button>
|
|
<n-button @click="importModalVisible = true">
|
|
<template #icon><n-icon><CloudUploadOutline /></n-icon></template>
|
|
导入
|
|
</n-button>
|
|
<n-button @click="handleExport">
|
|
<template #icon><n-icon><DownloadOutline /></n-icon></template>
|
|
导出{{ selectedIds.length > 0 ? `(${selectedIds.length})` : '' }}
|
|
</n-button>
|
|
<n-button type="error" :disabled="selectedIds.length === 0" @click="handleBatchDelete">
|
|
<template #icon><n-icon><TrashOutline /></n-icon></template>
|
|
删除
|
|
</n-button>
|
|
</n-space>
|
|
</div> -->
|
|
|
|
<!-- 表格 -->
|
|
<n-data-table
|
|
:columns="columns"
|
|
:data="tableData"
|
|
:loading="loading"
|
|
:pagination="pagination"
|
|
:row-key="(row) => row.id"
|
|
:scroll-x="1200"
|
|
@update:page="handlePageChange"
|
|
@update:page-size="handlePageSizeChange"
|
|
@update:checked-row-keys="handleCheck"
|
|
/>
|
|
</n-card>
|
|
|
|
<!-- 新增/编辑弹窗 -->
|
|
<n-modal v-model:show="modalVisible" preset="card" :title="modalTitle" style="width: 600px">
|
|
<n-form ref="formRef" :model="formData" :rules="formRules" label-placement="left" label-width="100px">
|
|
<n-form-item label="当前序" path="currentlyPrefaceName">
|
|
<n-input v-model:value="formData.currentlyPrefaceName" disabled />
|
|
</n-form-item>
|
|
<n-form-item label="下一序" path="nextPrefaceName">
|
|
<n-input v-model:value="formData.nextPrefaceName" disabled/>
|
|
</n-form-item>
|
|
<n-form-item label="所属业务" path="businessName">
|
|
<n-input v-model:value="formData.businessName" disabled/>
|
|
</n-form-item>
|
|
<n-form-item label="流转类型" path="flowingAroundType">
|
|
<n-select v-model:value="formData.flowingAroundType" :options="flowingAroundTypeOptions" disabled />
|
|
</n-form-item>
|
|
<n-form-item label="流转数量" path="quantity">
|
|
<n-input v-model:value="formData.quantity" placeholder="请输入流转数量" disabled />
|
|
</n-form-item>
|
|
<n-form-item label="确认结果" path="confirmResult">
|
|
<n-select v-model:value="formData.confirmResult" placeholder="请选择确认结果" :options="confirmResultOptions" />
|
|
</n-form-item>
|
|
<n-form-item label="原因" path="reason">
|
|
<n-input
|
|
v-model:value="formData.reason"
|
|
type="textarea"
|
|
placeholder="拒绝时必须填写原因"
|
|
:autosize= "{ minRows: 2, maxRows: 5 }"
|
|
/>
|
|
</n-form-item>
|
|
|
|
</n-form>
|
|
<template #footer>
|
|
<n-space justify="end">
|
|
<n-button @click="modalVisible = false">取消</n-button>
|
|
<n-button type="primary" @click="handleSubmit">确定</n-button>
|
|
</n-space>
|
|
</template>
|
|
</n-modal>
|
|
|
|
<n-modal v-model:show="modalCheckVisible" preset="card" :title="modalCheckTitle" :style="parentWidth" >
|
|
<AssingWorkVue v-if="modalCheckData?.businessCode === 'mes_assing_work'"
|
|
:id="modalCheckData.businessId"
|
|
:num="modalCheckData.quantity"
|
|
:qcNumDisabled ="true"
|
|
@updateWidth="handleUpdateWidth"/>
|
|
<SubmitDetailPage
|
|
v-if="modalCheckData?.businessCode === 'mes_quality_testing'"
|
|
:id="modalCheckData.businessId"
|
|
:disabled="true"
|
|
/>
|
|
</n-modal>
|
|
|
|
<!-- 导入弹窗 -->
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, h, onMounted, computed } from 'vue'
|
|
import { NButton, NSpace, NIcon, NUpload, useMessage, useDialog, type DataTableColumns, type UploadCustomRequestOptions } from 'naive-ui'
|
|
import { SearchOutline, RefreshOutline, AddOutline, TrashOutline, CreateOutline, EyeOutline, DownloadOutline } from '@vicons/ionicons5'
|
|
import { flowingAroundApi, type FlowingAround } from '@/api/flowingAround'
|
|
import { dictDataApi } from '@/api/org'
|
|
import AssingWorkVue from '@/views/biz/flowingAround/assingWork.vue'
|
|
import SubmitDetailPage from '@/components/SubmitDetailPage.vue'
|
|
|
|
const message = useMessage()
|
|
const dialog = useDialog()
|
|
|
|
// 搜索表单
|
|
const searchForm = reactive({
|
|
})
|
|
|
|
// 表格数据
|
|
const tableData = ref<FlowingAround[]>([])
|
|
const loading = ref(false)
|
|
const selectedIds = ref<number[]>([])
|
|
const pagination = reactive({
|
|
page: 1,
|
|
pageSize: 10,
|
|
itemCount: 0,
|
|
showSizePicker: true,
|
|
pageSizes: [10, 20, 50]
|
|
})
|
|
|
|
// 弹窗
|
|
const modalVisible = ref(false)
|
|
const modalTitle = ref('')
|
|
const importModalVisible = ref(false)
|
|
const formRef = ref()
|
|
const defaultFormData: FlowingAround = {
|
|
currentlyPrefaceId: undefined,
|
|
nextPrefaceId: undefined,
|
|
businessId: undefined,
|
|
businessCode: '',
|
|
quantity: undefined,
|
|
confirmUserId: undefined,
|
|
confirmTime: undefined,
|
|
submitter: undefined,
|
|
submitTime: undefined,
|
|
}
|
|
const formData = reactive<FlowingAround>({ ...defaultFormData })
|
|
|
|
// 字典选项(下拉框/单选框/复选框关联字典时使用)
|
|
|
|
// 表单校验规则
|
|
const formRules = {
|
|
reason: [
|
|
{
|
|
required: true,
|
|
message: '拒绝时必须填写原因',
|
|
trigger: ['blur', 'change'],
|
|
validator: (rule, value, callback) => {
|
|
// 拿到表单整体数据,判断审核结果
|
|
if (formData.confirmResult === '拒绝' && (!value || !String(value).trim())) {
|
|
callback(new Error('拒绝时原因不能为空'))
|
|
} else {
|
|
callback()
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
// 表格列
|
|
const columns: DataTableColumns<FlowingAround> = [
|
|
{ type: 'selection' },
|
|
{ title: '物料名称', key: 'materialName' },
|
|
{ title: '物料编码', key: 'materialCode' },
|
|
{ title: '当前序ID', key: 'currentlyPrefaceName' },
|
|
{ title: '下一序ID', key: 'nextPrefaceName' },
|
|
{ title: '业务来源', key: 'businessName' },
|
|
{ title: '流转数量', key: 'quantity' },
|
|
{ title: '流转来源', key: 'flowingAroundType', render(row){
|
|
const val = row.flowingAroundType
|
|
const opt = flowingAroundTypeOptions.value.find(o => o.value === val || String(o.value) === String(val))
|
|
return opt ? opt.label : (val ?? '-')
|
|
} },
|
|
{ title: '提交人', key: 'submitterName' },
|
|
{ title: '提交时间', key: 'submitTime' },
|
|
{ title: '提交信息', key: 'info' ,
|
|
render(row) {
|
|
return h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'nowrap' } }, [
|
|
h(NButton, { size: 'small', quaternary: true, onClick: () => handleView(row) }, {
|
|
default: () => [h(NIcon, null, { default: () => h(EyeOutline) }), ' 查看']
|
|
})
|
|
])
|
|
}
|
|
},
|
|
{ title: '确认人', key: 'confirmUserName' },
|
|
{ title: '确认时间', key: 'confirmTime' },
|
|
{ title: '确认结果', key: 'confirmResult' },
|
|
{ title: '原因', key: 'reason' },
|
|
{
|
|
title: '操作',
|
|
key: 'actions',
|
|
width: 140,
|
|
fixed: 'right',
|
|
render(row) {
|
|
return h('div', { style: { display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'nowrap' } }, [
|
|
h(NButton, { size: 'small', quaternary: true, onClick: () => handleEdit(row), disabled: row.confirmResult !== '待确认' }, {
|
|
default: () => [h(NIcon, null, { default: () => h(CreateOutline) }), ' 流转确认']
|
|
})
|
|
])
|
|
}
|
|
}
|
|
]
|
|
|
|
// 加载数据
|
|
async function loadData() {
|
|
loading.value = true
|
|
try {
|
|
const res = await flowingAroundApi.page({
|
|
page: pagination.page,
|
|
pageSize: pagination.pageSize,
|
|
})
|
|
tableData.value = res.list
|
|
pagination.itemCount = res.total
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
|
|
let parentWidth = ref('width: 20%;')
|
|
|
|
|
|
function handleUpdateWidth(width:string){
|
|
parentWidth.value =width;
|
|
}
|
|
|
|
// 搜索
|
|
function handleSearch() {
|
|
pagination.page = 1
|
|
loadData()
|
|
}
|
|
|
|
// 重置
|
|
function handleReset() {
|
|
handleSearch()
|
|
}
|
|
|
|
// 分页
|
|
function handlePageChange(page: number) {
|
|
pagination.page = page
|
|
loadData()
|
|
}
|
|
|
|
function handlePageSizeChange(pageSize: number) {
|
|
pagination.pageSize = pageSize
|
|
pagination.page = 1
|
|
loadData()
|
|
}
|
|
|
|
// 选择
|
|
function handleCheck(keys: Array<string | number>) {
|
|
selectedIds.value = keys as number[]
|
|
}
|
|
|
|
// 新增
|
|
function handleAdd() {
|
|
modalTitle.value = '新增流转记录表'
|
|
Object.assign(formData, defaultFormData)
|
|
modalVisible.value = true
|
|
}
|
|
|
|
const modalCheckVisible = ref(false)
|
|
const modalCheckData = ref<FlowingAround>()
|
|
const modalCheckTitle = ref('')
|
|
//查看提交数据
|
|
function handleView(row: FlowingAround){
|
|
console.log(row);
|
|
|
|
modalCheckTitle.value = '提交详情'
|
|
modalCheckData.value = row
|
|
modalCheckVisible.value = true
|
|
}
|
|
|
|
// 编辑
|
|
function handleEdit(row: FlowingAround) {
|
|
modalTitle.value = '流转确认审核'
|
|
Object.assign(formData, row)
|
|
if (formData.confirmTime && typeof formData.confirmTime === 'string') {
|
|
formData.confirmTime = new Date(formData.confirmTime.replace(' ', 'T')).getTime()
|
|
}
|
|
if (formData.submitTime && typeof formData.submitTime === 'string') {
|
|
formData.submitTime = new Date(formData.submitTime.replace(' ', 'T')).getTime()
|
|
}
|
|
modalVisible.value = true
|
|
}
|
|
|
|
// 提交
|
|
async function handleSubmit() {
|
|
await formRef.value?.validate()
|
|
try {
|
|
const submitData = { ...formData } as FlowingAround
|
|
if (typeof submitData.confirmTime === 'number') {
|
|
submitData.confirmTime = new Date(submitData.confirmTime).toISOString().slice(0, 19).replace('T', ' ')
|
|
}
|
|
if (typeof submitData.submitTime === 'number') {
|
|
submitData.submitTime = new Date(submitData.submitTime).toISOString().slice(0, 19).replace('T', ' ')
|
|
}
|
|
if (submitData.id) {
|
|
await flowingAroundApi.update(submitData)
|
|
message.success('修改成功')
|
|
} else {
|
|
await flowingAroundApi.create(submitData)
|
|
message.success('新增成功')
|
|
}
|
|
modalVisible.value = false
|
|
loadData()
|
|
} catch (error) {
|
|
// 错误已在拦截器处理
|
|
}
|
|
}
|
|
|
|
// 删除
|
|
function handleDelete(row: FlowingAround) {
|
|
dialog.warning({
|
|
title: '提示',
|
|
content: '确定要删除该记录吗?',
|
|
positiveText: '确定',
|
|
negativeText: '取消',
|
|
onPositiveClick: async () => {
|
|
try {
|
|
await flowingAroundApi.delete([row.id!])
|
|
message.success('删除成功')
|
|
loadData()
|
|
} catch (error) {
|
|
// 错误已在拦截器处理
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 批量删除
|
|
function handleBatchDelete() {
|
|
dialog.warning({
|
|
title: '提示',
|
|
content: `确定要删除选中的 ${selectedIds.value.length} 条记录吗?`,
|
|
positiveText: '确定',
|
|
negativeText: '取消',
|
|
onPositiveClick: async () => {
|
|
try {
|
|
await flowingAroundApi.delete(selectedIds.value)
|
|
message.success('删除成功')
|
|
selectedIds.value = []
|
|
loadData()
|
|
} catch (error) {
|
|
// 错误已在拦截器处理
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 导出
|
|
async function handleExport() {
|
|
try {
|
|
const params: Record<string, any> = {}
|
|
if (selectedIds.value.length > 0) params.ids = selectedIds.value
|
|
const blob = await flowingAroundApi.export(params)
|
|
const url = window.URL.createObjectURL(blob)
|
|
const link = document.createElement('a')
|
|
link.href = url
|
|
link.download = '流转记录表数据.xlsx'
|
|
link.click()
|
|
window.URL.revokeObjectURL(url)
|
|
} catch (error) {
|
|
// 错误已在拦截器处理
|
|
}
|
|
}
|
|
|
|
// 下载导入模板
|
|
async function handleDownloadTemplate() {
|
|
try {
|
|
const blob = await flowingAroundApi.downloadTemplate()
|
|
const url = window.URL.createObjectURL(blob)
|
|
const link = document.createElement('a')
|
|
link.href = url
|
|
link.download = '流转记录表导入模板.xlsx'
|
|
link.click()
|
|
window.URL.revokeObjectURL(url)
|
|
} catch (error) {
|
|
// 错误已在拦截器处理
|
|
}
|
|
}
|
|
|
|
// 导入上传
|
|
async function handleImportUpload({ file }: UploadCustomRequestOptions) {
|
|
if (!file.file) return
|
|
try {
|
|
const result = await flowingAroundApi.importData(file.file)
|
|
if (result.fail > 0) {
|
|
dialog.warning({
|
|
title: '导入结果',
|
|
content: `成功: ${result.success} 条,失败: ${result.fail} 条\n错误信息: ${(result.errors || []).join('\n') || '无'}`,
|
|
positiveText: '确定'
|
|
})
|
|
} else {
|
|
message.success(`导入成功,共 ${result.success} 条数据`)
|
|
importModalVisible.value = false
|
|
}
|
|
loadData()
|
|
} catch (error) {
|
|
// 错误已在拦截器处理
|
|
}
|
|
}
|
|
|
|
const flowingAroundTypeOptions = ref<{ label: string; value: any }[]>([])
|
|
|
|
const confirmResultOptions = computed<any[]>(() => {
|
|
const baseOptions: any[] = [
|
|
{label: "待确认", value: "待确认"},
|
|
{label: "同意", value: "同意"},
|
|
]
|
|
|
|
//当 formData.businessCode !== 'mes_quality_testing' 时,才追加“拒绝”选项
|
|
console.log(formData.businessCode)
|
|
if (formData.businessCode !== 'mes_quality_testing') {
|
|
baseOptions.push( {label: "拒绝", value: "拒绝"},)
|
|
}
|
|
|
|
return baseOptions
|
|
})
|
|
|
|
|
|
// 加载字典选项
|
|
async function loadDictOptions() {
|
|
try {
|
|
const data = await dictDataApi.listByType('flowing_around_type')
|
|
flowingAroundTypeOptions.value = data.map(d => ({ label: d.dictLabel, value: (Number(d.dictValue) || d.dictValue) }))
|
|
} catch {}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadData()
|
|
loadDictOptions()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.search-form {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.table-toolbar {
|
|
margin-bottom: 16px;
|
|
}
|
|
</style>
|