生产管理(未完成1)

This commit is contained in:
zc
2026-06-24 11:09:57 +08:00
parent 5c3721a88c
commit 04e0b4f3b5
13 changed files with 1537 additions and 1 deletions

56
src/apis/bom/bom.ts Normal file
View File

@@ -0,0 +1,56 @@
import http from '@/utils/http'
const BASE_URL = '/bom/bom'
export interface BomResp {
id: string
bomCode: string
bomName: string
bomType: string
remark: string
createUser: string
createTime: string
updateUser: string
updateTime: string
createUserString: string
updateUserString: string
disabled: boolean
}
export interface BomQuery {
bomCode: string | undefined
bomName: string | undefined
bomType: string | undefined
createTime: string | undefined
sort: Array<string>
}
export interface BomPageQuery extends BomQuery, PageQuery {}
/** @desc 查询BOM物料清单列表 */
export function listBom(query: BomPageQuery) {
return http.get<PageRes<BomResp[]>>(`${BASE_URL}`, query)
}
/** @desc 查询BOM物料清单详情 */
export function getBom(id: string) {
return http.get<BomResp>(`${BASE_URL}/${id}`)
}
/** @desc 新增BOM物料清单 */
export function addBom(data: any) {
return http.post(`${BASE_URL}`, data)
}
/** @desc 修改BOM物料清单 */
export function updateBom(data: any, id: string) {
return http.put(`${BASE_URL}/${id}`, data)
}
/** @desc 删除BOM物料清单 */
export function deleteBom(id: string) {
return http.del(`${BASE_URL}/${id}`)
}
/** @desc 导出BOM物料清单 */
export function exportBom(query: BomQuery) {
return http.download(`${BASE_URL}/export`, query)
}

View File

@@ -0,0 +1,86 @@
import http from '@/utils/http'
const BASE_URL = '/process/process'
export interface ProcessResp {
id: string
processCode: string
processName: string
processType: string
processCategory: string
isKeyProcess: string
isSpecialProcess: string
isQualityGate: string
standardDurationMinutes: string
setupDurationMinutes: string
waitingDurationMinutes: string
moveDurationMinutes: string
defaultWorkCenter: string
requiredSkillLevel: string
minOperatorCount: string
parameterTemplate: string
qualityCheckRequired: string
defaultCheckRate: string
allowableDefectRate: string
needMaterial: string
materialIssueMethod: string
status: string
version: string
sortOrder: string
sopUrl: string
videoUrl: string
remark: string
attachmentIds: string
createUser: string
createTime: string
updateUser: string
updateTime: string
createUserString: string
updateUserString: string
disabled: boolean
}
export interface ProcessQuery {
processCode: string | undefined
processName: string | undefined
processType: string | undefined
isKeyProcess: string | undefined
isSpecialProcess: string | undefined
isQualityGate: string | undefined
qualityCheckRequired: string | undefined
needMaterial: string | undefined
status: string | undefined
version: string | undefined
createTime: string | undefined
sort: Array<string>
}
export interface ProcessPageQuery extends ProcessQuery, PageQuery {}
/** @desc 查询工序基础信息列表 */
export function listProcess(query: ProcessPageQuery) {
return http.get<PageRes<ProcessResp[]>>(`${BASE_URL}`, query)
}
/** @desc 查询工序基础信息详情 */
export function getProcess(id: string) {
return http.get<ProcessResp>(`${BASE_URL}/${id}`)
}
/** @desc 新增工序基础信息 */
export function addProcess(data: any) {
return http.post(`${BASE_URL}`, data)
}
/** @desc 修改工序基础信息 */
export function updateProcess(data: any, id: string) {
return http.put(`${BASE_URL}/${id}`, data)
}
/** @desc 删除工序基础信息 */
export function deleteProcess(id: string) {
return http.del(`${BASE_URL}/${id}`)
}
/** @desc 导出工序基础信息 */
export function exportProcess(query: ProcessQuery) {
return http.download(`${BASE_URL}/export`, query)
}

View File

@@ -38,7 +38,7 @@ export function listProductionLine(query: ProductionLinePageQuery) {
/** @desc 查询产线基础详情 */ /** @desc 查询产线基础详情 */
export function getProductionLine(id: string) { export function getProductionLine(id: string) {
return http.get<ProductionLineDetailResp>(`${BASE_URL}/${id}`) return http.get<ProductionLineResp>(`${BASE_URL}/${id}`)
} }
/** @desc 新增产线基础 */ /** @desc 新增产线基础 */

View File

@@ -0,0 +1,62 @@
import http from '@/utils/http'
const BASE_URL = '/routing/routing'
export interface RoutingResp {
id: string
routingCode: string
routingName: string
productId: string
productCategory: string
lineId: string
version: string
isActive: string
status: string
remark: string
createUser: string
createTime: string
updateUser: string
updateTime: string
createUserString: string
updateUserString: string
disabled: boolean
}
export interface RoutingQuery {
routingCode: string | undefined
routingName: string | undefined
version: string | undefined
isActive: string | undefined
status: string | undefined
sort: Array<string>
}
export interface RoutingPageQuery extends RoutingQuery, PageQuery {}
/** @desc 查询工艺路线主列表 */
export function listRouting(query: RoutingPageQuery) {
return http.get<PageRes<RoutingResp[]>>(`${BASE_URL}`, query)
}
/** @desc 查询工艺路线主详情 */
export function getRouting(id: string) {
return http.get<RoutingResp>(`${BASE_URL}/${id}`)
}
/** @desc 新增工艺路线主 */
export function addRouting(data: any) {
return http.post(`${BASE_URL}`, data)
}
/** @desc 修改工艺路线主 */
export function updateRouting(data: any, id: string) {
return http.put(`${BASE_URL}/${id}`, data)
}
/** @desc 删除工艺路线主 */
export function deleteRouting(id: string) {
return http.del(`${BASE_URL}/${id}`)
}
/** @desc 导出工艺路线主 */
export function exportRouting(query: RoutingQuery) {
return http.download(`${BASE_URL}/export`, query)
}

View File

@@ -0,0 +1,129 @@
<template>
<a-modal
v-model:visible="visible"
:title="title"
:mask-closable="false"
:esc-to-close="false"
:width="width >= 600 ? 600 : '100%'"
draggable
@before-ok="save"
@close="reset"
>
<GiForm ref="formRef" v-model="form" :columns="columns" />
</a-modal>
</template>
<script setup lang="ts">
import { Message } from '@arco-design/web-vue'
import { useWindowSize } from '@vueuse/core'
import { getBom, addBom, updateBom } from '@/apis/bom/bom'
import { type ColumnItem, GiForm } from '@/components/GiForm'
import { useResetReactive } from '@/hooks'
import { useDict } from '@/hooks/app'
const emit = defineEmits<{
(e: 'save-success'): void
}>()
const { width } = useWindowSize()
const dataId = ref('')
const visible = ref(false)
const isUpdate = computed(() => !!dataId.value)
const title = computed(() => (isUpdate.value ? '修改BOM物料' : '新增BOM物料'))
const formRef = ref<InstanceType<typeof GiForm>>()
const [form, resetForm] = useResetReactive({})
const { bom_type } = useDict('bom_type')
const columns: ColumnItem[] = reactive([
{
label: '编号',
field: 'bomCode',
type: 'input',
props: {
placeholder: '请输入BOM编码',
maxLength: 50
},
span: 24,
required: true,
},
{
label: '名称',
field: 'bomName',
type: 'input',
props: {
placeholder: '请输入BOM名称',
maxLength: 50
},
span: 24,
required: true,
},
{
label: '类型',
field: 'bomType',
type: 'select',
span: 24,
required: true,
props: {
options: bom_type
}
},
{
label: '说明',
field: 'remark',
type: 'textarea',
props: {
rows: 4
},
span: 24,
},
])
// 重置
const reset = () => {
formRef.value?.formRef?.resetFields()
resetForm()
}
// 保存
const save = async () => {
try {
const isInvalid = await formRef.value?.formRef?.validate()
if (isInvalid) return false
if (isUpdate.value) {
await updateBom(form, dataId.value)
Message.success('修改成功')
} else {
await addBom(form)
Message.success('新增成功')
}
emit('save-success')
return true
} catch (error) {
return false
}
}
// 新增
const onAdd = async () => {
reset()
dataId.value = ''
visible.value = true
}
// 修改
const onUpdate = async (id: string) => {
reset()
dataId.value = id
const { data } = await getBom(id)
Object.assign(form, data)
visible.value = true
}
defineExpose({ onAdd, onUpdate })
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,50 @@
<template>
<a-drawer v-model:visible="visible" title="BOM物料清单详情" :width="width >= 600 ? 600 : '100%'" :footer="false">
<a-descriptions :column="2" size="large" class="general-description">
<a-descriptions-item label="主键ID">{{ dataDetail?.id }}</a-descriptions-item>
<a-descriptions-item label="编号">{{ dataDetail?.bomCode }}</a-descriptions-item>
<a-descriptions-item label="名称">{{ dataDetail?.bomName }}</a-descriptions-item>
<a-descriptions-item label="类型">
<GiCellTag :value="dataDetail?.bomType" :dict="bom_type" />
</a-descriptions-item>
<a-descriptions-item label="说明">{{ dataDetail?.remark }}</a-descriptions-item>
<a-descriptions-item label="">{{ dataDetail?.createUser }}</a-descriptions-item>
<a-descriptions-item label="创建人">{{ dataDetail?.createUserString }}</a-descriptions-item>
<a-descriptions-item label="">{{ dataDetail?.createTime }}</a-descriptions-item>
<a-descriptions-item label="">{{ dataDetail?.updateUser }}</a-descriptions-item>
<a-descriptions-item label="修改人">{{ dataDetail?.updateUserString }}</a-descriptions-item>
<a-descriptions-item label="">{{ dataDetail?.updateTime }}</a-descriptions-item>
</a-descriptions>
</a-drawer>
</template>
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core'
import { type BomResp, getBom as getDetail } from '@/apis/bom/bom'
import {useDict} from "@/hooks/app";
const { bom_type } = useDict('bom_type')
const { width } = useWindowSize()
const dataId = ref('')
const dataDetail = ref<BomResp>()
const visible = ref(false)
// 查询详情
const getDataDetail = async () => {
const { data } = await getDetail(dataId.value)
dataDetail.value = data
}
// 打开
const onOpen = async (id: string) => {
dataId.value = id
await getDataDetail()
visible.value = true
}
defineExpose({ onOpen })
</script>
<style scoped lang="scss"></style>

157
src/views/bom/bom/index.vue Normal file
View File

@@ -0,0 +1,157 @@
<template>
<div class="gi_table_page">
<GiTable
row-key="id"
:data="dataList"
:columns="columns"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="pagination"
:disabled-tools="['size']"
:disabled-column-keys="['name']"
@refresh="search"
>
<template #toolbar-left>
<a-input-search v-model="queryForm.bomCode" placeholder="请输入编号" allow-clear @search="search" />
<a-input-search v-model="queryForm.bomName" placeholder="请输入名称" allow-clear @search="search" />
<a-select
v-model="queryForm.bomType"
placeholder="请选择类型"
:options="bomTypeDict"
allow-clear
style="width: 150px"
@change="search"
/>
<a-input-search v-model="queryForm.createTime" placeholder="请输入" allow-clear @search="search" />
<a-button @click="reset">
<template #icon><icon-refresh /></template>
<template #default>重置</template>
</a-button>
</template>
<template #toolbar-right>
<a-button v-permission="['bom:bom:add']" type="primary" @click="onAdd">
<template #icon><icon-plus /></template>
<template #default>新增</template>
</a-button>
<a-button v-permission="['bom:bom:export']" @click="onExport">
<template #icon><icon-download /></template>
<template #default>导出</template>
</a-button>
</template>
<template #bomType="{ record }">
<GiCellTag :value="record.bomType" :dict="bom_type" />
</template>
<template #action="{ record }">
<a-space>
<a-link v-permission="['bom:bom:detail']" title="详情" @click="onDetail(record)">详情</a-link>
<a-link v-permission="['bom:bom:update']" title="修改" @click="onUpdate(record)">修改</a-link>
<a-link
v-permission="['bom:bom:delete']"
status="danger"
:disabled="record.disabled"
:title="record.disabled ? '不可删除' : '删除'"
@click="onDelete(record)"
>
删除
</a-link>
</a-space>
</template>
</GiTable>
<BomAddModal ref="BomAddModalRef" @save-success="search" />
<BomDetailDrawer ref="BomDetailDrawerRef" />
</div>
</template>
<script setup lang="ts">
import BomAddModal from './BomAddModal.vue'
import BomDetailDrawer from './BomDetailDrawer.vue'
import { type BomResp, type BomQuery, deleteBom, exportBom, listBom } from '@/apis/bom/bom'
import type { TableInstanceColumns } from '@/components/GiTable/type'
import { useDownload, useTable } from '@/hooks'
import GiCellTag from '@/components/GiCell/GiCellTag.vue'
import { isMobile } from '@/utils'
import has from '@/utils/has'
import {useDict} from "@/hooks/app";
const { bom_type } = useDict('bom_type')
defineOptions({ name: 'Bom' })
const queryForm = reactive<BomQuery>({
bomCode: undefined,
bomName: undefined,
bomType: undefined,
createTime: undefined,
sort: ['id,desc']
})
const {
tableData: dataList,
loading,
pagination,
search,
handleDelete
} = useTable((page) => listBom({ ...queryForm, ...page }), { immediate: true })
const columns = ref<TableInstanceColumns[]>([
{ title: 'BOM编码', dataIndex: 'bomCode', slotName: 'bomCode' },
{ title: 'BOM名称', dataIndex: 'bomName', slotName: 'bomName' },
{ title: '类型', dataIndex: 'bomType', slotName: 'bomType', align: 'center' },
{ title: '说明', dataIndex: 'remark', slotName: 'remark' },
{ title: '创建人', dataIndex: 'createUserString', slotName: 'createUser' },
{ title: '创建时间', dataIndex: 'createTime', slotName: 'createTime' },
{ title: '更新人', dataIndex: 'updateUserString', slotName: 'updateUser', show: false },
{ title: '更新时间', dataIndex: 'updateTime', slotName: 'updateTime', show: false },
{
title: '操作',
dataIndex: 'action',
slotName: 'action',
width: 160,
align: 'center',
fixed: !isMobile() ? 'right' : undefined,
show: has.hasPermOr(['bom:bom:detail', 'bom:bom:update', 'bom:bom:delete'])
}
]);
// 重置
const reset = () => {
queryForm.bomCode = undefined
queryForm.bomName = undefined
queryForm.bomType = undefined
queryForm.createTime = undefined
search()
}
// 删除
const onDelete = (record: BomResp) => {
return handleDelete(() => deleteBom(record.id), {
content: `是否确定删除该条数据?`,
showModal: true
})
}
// 导出
const onExport = () => {
useDownload(() => exportBom(queryForm))
}
const BomAddModalRef = ref<InstanceType<typeof BomAddModal>>()
// 新增
const onAdd = () => {
BomAddModalRef.value?.onAdd()
}
// 修改
const onUpdate = (record: BomResp) => {
BomAddModalRef.value?.onUpdate(record.id)
}
const BomDetailDrawerRef = ref<InstanceType<typeof BomDetailDrawer>>()
// 详情
const onDetail = (record: BomResp) => {
BomDetailDrawerRef.value?.onOpen(record.id)
}
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,293 @@
<template>
<a-modal
v-model:visible="visible"
:title="title"
:mask-closable="false"
:esc-to-close="false"
:width="width >= 700 ? 700 : '100%'"
draggable
@before-ok="save"
@close="reset"
>
<GiForm ref="formRef" v-model="form" :columns="columns" />
</a-modal>
</template>
<script setup lang="tsx">
import { Message } from '@arco-design/web-vue'
import { useWindowSize } from '@vueuse/core'
import { getProcess, addProcess, updateProcess } from '@/apis/process/process'
import { type ColumnItem, GiForm } from '@/components/GiForm'
import { useResetReactive } from '@/hooks'
import { useDict } from '@/hooks/app'
const emit = defineEmits<{
(e: 'save-success'): void
}>()
const { width } = useWindowSize()
const dataId = ref('')
const visible = ref(false)
const isUpdate = computed(() => !!dataId.value)
const title = computed(() => (isUpdate.value ? '修改工序基础信息' : '新增工序基础信息'))
const formRef = ref<InstanceType<typeof GiForm>>()
const { process_type, yse_or_no, process_status, material_issue_method, skill_level } = useDict(
'process_type',
'yse_or_no',
'process_status',
'material_issue_method',
'skill_level'
)
const [form, resetForm] = useResetReactive({
processCode: '',
processName: '',
processType: '',
processCategory: '',
isKeyProcess: '',
isSpecialProcess: '',
standardDurationMinutes: '',
setupDurationMinutes: '',
waitingDurationMinutes: '',
moveDurationMinutes: '',
requiredSkillLevel: '',
minOperatorCount: '',
qualityCheckRequired: '',
defaultCheckRate: '',
allowableDefectRate: '',
needMaterial: '',
materialIssueMethod: '',
status: '',
remark: '',
attachmentIds: ''
})
const columns: ColumnItem[] = reactive([
{
label: '工序编号',
field: 'processCode',
type: 'input',
span: 24,
required: true,
placeholder: '请输入工序编号'
},
{
label: '工序名称',
field: 'processName',
type: 'input',
span: 24,
required: true,
placeholder: '请输入工序名称'
},
{
label: '工序类型',
field: 'processType',
type: 'select',
span: 24,
required: true,
props: {
options: process_type,
},
placeholder: '请选择工序类型'
},
{
label: '工序分类',
field: 'processCategory',
type: 'input',
span: 24,
placeholder: '如SMT、组装、测试'
},
{
label: '是否关键工序',
field: 'isKeyProcess',
type: 'select',
span: 12,
required: true,
props: {
options: yse_or_no,
},
placeholder: '是否关键工序'
},
{
label: '特殊工序',
field: 'isSpecialProcess',
type: 'select',
span: 12,
required: true,
props: {
options: yse_or_no,
},
placeholder: '是否特殊工序'
},
{
label: '需要质检',
field: 'qualityCheckRequired',
type: 'select',
span: 12,
required: true,
props: {
options: yse_or_no,
},
placeholder: '是否需要质检'
},
{
label: '标准工时(分钟)',
field: 'standardDurationMinutes',
type: 'input-number',
span: 12,
placeholder: '请输入标准工时'
},
{
label: '准备时间(分钟)',
field: 'setupDurationMinutes',
type: 'input-number',
span: 12,
placeholder: '请输入准备时间'
},
{
label: '等待时间(分钟)',
field: 'waitingDurationMinutes',
type: 'input-number',
span: 12,
placeholder: '请输入等待时间'
},
{
label: '搬运时间(分钟)',
field: 'moveDurationMinutes',
type: 'input-number',
span: 12,
placeholder: '请输入搬运时间'
},
{
label: '技能等级',
field: 'requiredSkillLevel',
type: 'select',
span: 12,
dict: skill_level,
placeholder: '请选择技能等级'
},
{
label: '最少人数',
field: 'minOperatorCount',
type: 'input-number',
span: 12,
props: {
min: 1,
},
placeholder: '请输入最少人数'
},
{
label: '抽检比例(%)',
field: 'defaultCheckRate',
type: 'input-number',
span: 12,
props: {
min: 0,
step: 0.1
},
slots: {
append: () => (
<span style={{ width: '30px', textAlign: 'center' }}>%</span>
),
},
placeholder: '请输入抽检比例'
},
{
label: '不良率(%)',
field: 'allowableDefectRate',
type: 'input-number',
span: 12,
props: {
min: 0,
step: 0.1
},
placeholder: '请输入不良率'
},
{
label: '需要领料',
field: 'needMaterial',
type: 'select',
span: 12,
required: true,
props: {
options: yse_or_no,
},
placeholder: '是否需要领料'
},
{
label: '发料方式',
field: 'materialIssueMethod',
type: 'select',
span: 12,
props: {
options: material_issue_method,
},
placeholder: '请选择发料方式'
},
{
label: '状态',
field: 'status',
type: 'select',
span: 12,
required: true,
props: {
options: process_status,
},
placeholder: '请选择状态'
},
{
label: '备注',
field: 'remark',
type: 'textarea',
span: 24,
placeholder: '请输入备注'
}
])
// 重置
const reset = () => {
formRef.value?.formRef?.resetFields()
resetForm()
}
// 保存
const save = async () => {
try {
const isInvalid = await formRef.value?.formRef?.validate()
if (isInvalid) return false
if (isUpdate.value) {
await updateProcess(form, dataId.value)
Message.success('修改成功')
} else {
await addProcess(form)
Message.success('新增成功')
}
emit('save-success')
return true
} catch (error) {
return false
}
}
// 新增
const onAdd = async () => {
reset()
dataId.value = ''
visible.value = true
}
// 修改
const onUpdate = async (id: string) => {
reset()
dataId.value = id
const { data } = await getProcess(id)
Object.assign(form, data)
visible.value = true
}
defineExpose({ onAdd, onUpdate })
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,83 @@
<template>
<a-drawer v-model:visible="visible" title="工序基础信息详情" :width="width >= 600 ? 600 : '100%'" :footer="false">
<a-descriptions :column="2" size="large" class="general-description">
<a-descriptions-item label="工序编号">{{ dataDetail?.processCode }}</a-descriptions-item>
<a-descriptions-item label="工序名称">{{ dataDetail?.processName }}</a-descriptions-item>
<a-descriptions-item label="工序类型">
<GiCellTag :value="dataDetail?.processType" :dict="process_type" />
</a-descriptions-item>
<a-descriptions-item label="工序分类">{{ dataDetail?.processCategory }}</a-descriptions-item>
<a-descriptions-item label="关键工序">
<GiCellTag :value="dataDetail?.isKeyProcess" :dict="yse_or_no" />
</a-descriptions-item>
<a-descriptions-item label="特殊工序">
<GiCellTag :value="dataDetail?.isSpecialProcess" :dict="yse_or_no" />
</a-descriptions-item>
<a-descriptions-item label="需要质检">
<GiCellTag :value="dataDetail?.qualityCheckRequired" :dict="yse_or_no" />
</a-descriptions-item>
<a-descriptions-item label="标准工时(分钟)">{{ dataDetail?.standardDurationMinutes }}</a-descriptions-item>
<a-descriptions-item label="准备时间(分钟)">{{ dataDetail?.setupDurationMinutes }}</a-descriptions-item>
<a-descriptions-item label="等待时间(分钟)">{{ dataDetail?.waitingDurationMinutes }}</a-descriptions-item>
<a-descriptions-item label="搬运时间(分钟)">{{ dataDetail?.moveDurationMinutes }}</a-descriptions-item>
<a-descriptions-item label="技能等级">
<GiCellTag :value="dataDetail?.requiredSkillLevel" :dict="skill_level" />
</a-descriptions-item>
<a-descriptions-item label="最少人数">{{ dataDetail?.minOperatorCount }}</a-descriptions-item>
<a-descriptions-item label="抽检比例(%)">{{ dataDetail?.defaultCheckRate }}</a-descriptions-item>
<a-descriptions-item label="不良率(%)">{{ dataDetail?.allowableDefectRate }}</a-descriptions-item>
<a-descriptions-item label="需要领料">
<GiCellTag :value="dataDetail?.needMaterial" :dict="yse_or_no" />
</a-descriptions-item>
<a-descriptions-item label="发料方式">
<GiCellTag :value="dataDetail?.materialIssueMethod" :dict="material_issue_method" />
</a-descriptions-item>
<a-descriptions-item label="状态">
<GiCellTag :value="dataDetail?.status" :dict="sys_normal_disable" />
</a-descriptions-item>
<a-descriptions-item label="备注">{{ dataDetail?.remark }}</a-descriptions-item>
<a-descriptions-item label="创建人">{{ dataDetail?.createUserString }}</a-descriptions-item>
<a-descriptions-item label="创建时间">{{ dataDetail?.createTime }}</a-descriptions-item>
<a-descriptions-item label="修改人">{{ dataDetail?.updateUserString }}</a-descriptions-item>
<a-descriptions-item label="修改时间">{{ dataDetail?.updateTime }}</a-descriptions-item>
</a-descriptions>
</a-drawer>
</template>
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core'
import { type ProcessResp, getProcess as getDetail } from '@/apis/process/process'
import GiCellTag from '@/components/GiCell/GiCellTag.vue'
import { useDict } from '@/hooks/app'
const { width } = useWindowSize()
const { process_type, yse_or_no, sys_normal_disable, material_issue_method, skill_level } = useDict(
'process_type',
'yse_or_no',
'sys_normal_disable',
'material_issue_method',
'skill_level'
)
const dataId = ref('')
const dataDetail = ref<ProcessResp>()
const visible = ref(false)
// 查询详情
const getDataDetail = async () => {
const { data } = await getDetail(dataId.value)
dataDetail.value = data
}
// 打开
const onOpen = async (id: string) => {
dataId.value = id
await getDataDetail()
visible.value = true
}
defineExpose({ onOpen })
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,255 @@
<template>
<div class="gi_table_page">
<GiTable
title="工序基础信息管理"
row-key="id"
:data="dataList"
:columns="columns"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="pagination"
:disabled-tools="['size']"
:disabled-column-keys="['name']"
@refresh="search"
>
<template #toolbar-left>
<a-input-search v-model="queryForm.processCode" placeholder="请输入工序编号" allow-clear @search="search" />
<a-input-search v-model="queryForm.processName" placeholder="请输入工序名称" allow-clear @search="search" />
<a-select
v-model="queryForm.processType"
placeholder="请选择工序类型"
:options="process_type"
allow-clear
style="width: 160px"
@change="search"
/>
<a-select
v-model="queryForm.isKeyProcess"
placeholder="是否关键工序"
:options="yse_or_no"
allow-clear
style="width: 150px"
@change="search"
/>
<a-select
v-model="queryForm.isSpecialProcess"
placeholder="是否特殊工序"
:options="yse_or_no"
allow-clear
style="width: 150px"
@change="search"
/>
<a-select
v-model="queryForm.isQualityGate"
placeholder="是否质量门禁"
:options="yse_or_no"
allow-clear
style="width: 150px"
@change="search"
/>
<a-select
v-model="queryForm.qualityCheckRequired"
placeholder="是否需要质检"
:options="yse_or_no"
allow-clear
style="width: 150px"
@change="search"
/>
<a-select
v-model="queryForm.needMaterial"
placeholder="是否需要领料"
:options="yse_or_no"
allow-clear
style="width: 150px"
@change="search"
/>
<a-select
v-model="queryForm.status"
placeholder="请选择状态"
:options="process_status"
allow-clear
style="width: 150px"
@change="search"
/>
<DateRangePicker v-model="queryForm.createTime" @change="search" />
<a-button @click="reset">
<template #icon><icon-refresh /></template>
<template #default>重置</template>
</a-button>
</template>
<template #toolbar-right>
<a-button v-permission="['process:process:add']" type="primary" @click="onAdd">
<template #icon><icon-plus /></template>
<template #default>新增</template>
</a-button>
<a-button v-permission="['process:process:export']" @click="onExport">
<template #icon><icon-download /></template>
<template #default>导出</template>
</a-button>
</template>
<template #processType="{ record }">
<GiCellTag :value="record.processType" :dict="process_type" />
</template>
<template #isKeyProcess="{ record }">
<GiCellTag :value="record.isKeyProcess" :dict="yse_or_no" />
</template>
<template #isSpecialProcess="{ record }">
<GiCellTag :value="record.isSpecialProcess" :dict="yse_or_no" />
</template>
<template #isQualityGate="{ record }">
<GiCellTag :value="record.isQualityGate" :dict="yse_or_no" />
</template>
<template #qualityCheckRequired="{ record }">
<GiCellTag :value="record.qualityCheckRequired" :dict="yse_or_no" />
</template>
<template #needMaterial="{ record }">
<GiCellTag :value="record.needMaterial" :dict="yse_or_no" />
</template>
<template #materialIssueMethod="{ record }">
<GiCellTag :value="record.materialIssueMethod" :dict="material_issue_method" />
</template>
<template #requiredSkillLevel="{ record }">
<GiCellTag :value="record.requiredSkillLevel" :dict="skill_level" />
</template>
<template #status="{ record }">
<GiCellTag :value="record.status" :dict="process_status" />
</template>
<template #action="{ record }">
<a-space>
<a-link v-permission="['process:process:detail']" title="详情" @click="onDetail(record)">详情</a-link>
<a-link v-permission="['process:process:update']" title="修改" @click="onUpdate(record)">修改</a-link>
<a-link v-permission="['process:process:update']" title="所需用料" @click="onUpdate(record)">所需用料</a-link>
<a-link
v-permission="['process:process:delete']"
status="danger"
:disabled="record.disabled"
:title="record.disabled ? '不可删除' : '删除'"
@click="onDelete(record)"
>
删除
</a-link>
</a-space>
</template>
</GiTable>
<ProcessAddModal ref="ProcessAddModalRef" @save-success="search" />
<ProcessDetailDrawer ref="ProcessDetailDrawerRef" />
</div>
</template>
<script setup lang="ts">
import ProcessAddModal from './ProcessAddModal.vue'
import ProcessDetailDrawer from './ProcessDetailDrawer.vue'
import { type ProcessResp, type ProcessQuery, deleteProcess, exportProcess, listProcess } from '@/apis/process/process'
import type { TableInstanceColumns } from '@/components/GiTable/type'
import GiCellTag from '@/components/GiCell/GiCellTag.vue'
import DateRangePicker from '@/components/DateRangePicker/index.vue'
import { useDownload, useTable } from '@/hooks'
import { useDict } from '@/hooks/app'
import { isMobile } from '@/utils'
import has from '@/utils/has'
defineOptions({ name: 'Process' })
const { process_type, yse_or_no, process_status, material_issue_method, skill_level } = useDict(
'process_type',
'yse_or_no',
'process_status',
'material_issue_method',
'skill_level'
)
const queryForm = reactive<ProcessQuery>({
processCode: undefined,
processName: undefined,
processType: undefined,
isKeyProcess: undefined,
isSpecialProcess: undefined,
isQualityGate: undefined,
qualityCheckRequired: undefined,
needMaterial: undefined,
status: undefined,
version: undefined,
createTime: undefined,
sort: ['id,desc']
})
const {
tableData: dataList,
loading,
pagination,
search,
handleDelete
} = useTable((page) => listProcess({ ...queryForm, ...page }), { immediate: true })
const columns = ref<TableInstanceColumns[]>([
{ title: '工序编号', dataIndex: 'processCode', slotName: 'processCode' },
{ title: '工序名称', dataIndex: 'processName', slotName: 'processName' },
{ title: '工序类型', dataIndex: 'processType', slotName: 'processType', align: 'center' },
{ title: '工序分类', dataIndex: 'processCategory', slotName: 'processCategory' },
{ title: '关键工序', dataIndex: 'isKeyProcess', slotName: 'isKeyProcess', align: 'center' },
{ title: '特殊工序', dataIndex: 'isSpecialProcess', slotName: 'isSpecialProcess', align: 'center' },
{ title: '需要质检', dataIndex: 'qualityCheckRequired', slotName: 'qualityCheckRequired', align: 'center' },
{ title: '需要领料', dataIndex: 'needMaterial', slotName: 'needMaterial', align: 'center' },
{ title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' },
{ title: '版本号', dataIndex: 'version', slotName: 'version' },
{ title: '创建人', dataIndex: 'createUserString', slotName: 'createUser' , show: false},
{ title: '创建时间', dataIndex: 'createTime', slotName: 'createTime', width: 180 },
{
title: '操作',
dataIndex: 'action',
slotName: 'action',
width: 160,
align: 'center',
fixed: !isMobile() ? 'right' : undefined,
show: has.hasPermOr(['process:process:detail', 'process:process:update', 'process:process:delete'])
}
]);
// 重置
const reset = () => {
queryForm.processCode = undefined
queryForm.processName = undefined
queryForm.processType = undefined
queryForm.isKeyProcess = undefined
queryForm.isSpecialProcess = undefined
queryForm.isQualityGate = undefined
queryForm.qualityCheckRequired = undefined
queryForm.needMaterial = undefined
queryForm.status = undefined
queryForm.version = undefined
queryForm.createTime = undefined
search()
}
// 删除
const onDelete = (record: ProcessResp) => {
return handleDelete(() => deleteProcess(record.id), {
content: `是否确定删除该条数据?`,
showModal: true
})
}
// 导出
const onExport = () => {
useDownload(() => exportProcess(queryForm))
}
const ProcessAddModalRef = ref<InstanceType<typeof ProcessAddModal>>()
// 新增
const onAdd = () => {
ProcessAddModalRef.value?.onAdd()
}
// 修改
const onUpdate = (record: ProcessResp) => {
ProcessAddModalRef.value?.onUpdate(record.id)
}
const ProcessDetailDrawerRef = ref<InstanceType<typeof ProcessDetailDrawer>>()
// 详情
const onDetail = (record: ProcessResp) => {
ProcessDetailDrawerRef.value?.onOpen(record.id)
}
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,149 @@
<template>
<a-modal
v-model:visible="visible"
:title="title"
:mask-closable="false"
:esc-to-close="false"
:width="width >= 600 ? 600 : '100%'"
draggable
@before-ok="save"
@close="reset"
>
<GiForm ref="formRef" v-model="form" :columns="columns" />
</a-modal>
</template>
<script setup lang="ts">
import { Message } from '@arco-design/web-vue'
import { useWindowSize } from '@vueuse/core'
import { getRouting, addRouting, updateRouting } from '@/apis/routing/routing'
import { type ColumnItem, GiForm } from '@/components/GiForm'
import { useResetReactive } from '@/hooks'
import { useDict } from '@/hooks/app'
const { yse_or_no, process_status } = useDict('yse_or_no', 'process_status')
const emit = defineEmits<{
(e: 'save-success'): void
}>()
const { width } = useWindowSize()
const dataId = ref('')
const visible = ref(false)
const isUpdate = computed(() => !!dataId.value)
const title = computed(() => (isUpdate.value ? '修改工艺路线主' : '新增工艺路线主'))
const formRef = ref<InstanceType<typeof GiForm>>()
const [form, resetForm] = useResetReactive({
routingCode: '',
routingName: '',
productId: '',
productCategory: '',
lineId: '',
version: '',
isActive: '',
status: '',
remark: ''
})
const columns: ColumnItem[] = reactive([
{
label: '工艺路线编码',
field: 'routingCode',
type: 'input',
span: 24,
required: true,
},
{
label: '工艺路线名称',
field: 'routingName',
type: 'input',
span: 24,
required: true,
},
{
label: '产品',
field: 'productId',
type: 'input',
span: 24,
},
{
label: '产线',
field: 'lineId',
type: 'input',
span: 24,
},
{
label: '是否启用',
field: 'isActive',
type: 'select',
span: 24,
required: true,
props: {
options: yse_or_no
}
},
{
label: '状态',
field: 'status',
type: 'select',
span: 24,
required: true,
props: {
options: process_status
}
},
{
label: '备注',
field: 'remark',
type: 'textarea',
span: 24,
},
])
// 重置
const reset = () => {
formRef.value?.formRef?.resetFields()
resetForm()
}
// 保存
const save = async () => {
try {
const isInvalid = await formRef.value?.formRef?.validate()
if (isInvalid) return false
if (isUpdate.value) {
await updateRouting(form, dataId.value)
Message.success('修改成功')
} else {
await addRouting(form)
Message.success('新增成功')
}
emit('save-success')
return true
} catch (error) {
return false
}
}
// 新增
const onAdd = async () => {
reset()
dataId.value = ''
visible.value = true
}
// 修改
const onUpdate = async (id: string) => {
reset()
dataId.value = id
const { data } = await getRouting(id)
Object.assign(form, data)
visible.value = true
}
defineExpose({ onAdd, onUpdate })
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,56 @@
<template>
<a-drawer v-model:visible="visible" title="工艺路线主详情" :width="width >= 600 ? 600 : '100%'" :footer="false">
<a-descriptions :column="2" size="large" class="general-description">
<a-descriptions-item label="主键ID">{{ dataDetail?.id }}</a-descriptions-item>
<a-descriptions-item label="工艺路线编码">{{ dataDetail?.routingCode }}</a-descriptions-item>
<a-descriptions-item label="工艺路线名称">{{ dataDetail?.routingName }}</a-descriptions-item>
<a-descriptions-item label="关联产品ID">{{ dataDetail?.productId }}</a-descriptions-item>
<a-descriptions-item label="适用产品分类">{{ dataDetail?.productCategory }}</a-descriptions-item>
<a-descriptions-item label="默认产线ID">{{ dataDetail?.lineId }}</a-descriptions-item>
<a-descriptions-item label="版本号">{{ dataDetail?.version }}</a-descriptions-item>
<a-descriptions-item label="是否启用">
<GiCellTag :value="dataDetail?.isActive" :dict="yse_or_no" />
</a-descriptions-item>
<a-descriptions-item label="状态">
<GiCellTag :value="dataDetail?.status" :dict="process_status" />
</a-descriptions-item>
<a-descriptions-item label="备注">{{ dataDetail?.remark }}</a-descriptions-item>
<a-descriptions-item label="创建人">{{ dataDetail?.createUserString }}</a-descriptions-item>
<a-descriptions-item label="创建时间">{{ dataDetail?.createTime }}</a-descriptions-item>
<a-descriptions-item label="修改人">{{ dataDetail?.updateUserString }}</a-descriptions-item>
<a-descriptions-item label="修改时间">{{ dataDetail?.updateTime }}</a-descriptions-item>
</a-descriptions>
</a-drawer>
</template>
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core'
import { type RoutingResp, getRouting as getDetail } from '@/apis/routing/routing'
import GiCellTag from '@/components/GiCell/GiCellTag.vue'
import { useDict } from '@/hooks/app'
const { yse_or_no, process_status } = useDict('yse_or_no', 'process_status')
const { width } = useWindowSize()
const dataId = ref('')
const dataDetail = ref<RoutingResp>()
const visible = ref(false)
// 查询详情
const getDataDetail = async () => {
const { data } = await getDetail(dataId.value)
dataDetail.value = data
}
// 打开
const onOpen = async (id: string) => {
dataId.value = id
await getDataDetail()
visible.value = true
}
defineExpose({ onOpen })
</script>
<style scoped lang="scss"></style>

View File

@@ -0,0 +1,160 @@
<template>
<div class="gi_table_page">
<GiTable
title="工艺路线主管理"
row-key="id"
:data="dataList"
:columns="columns"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="pagination"
:disabled-tools="['size']"
:disabled-column-keys="['name']"
@refresh="search"
>
<template #toolbar-left>
<a-input-search v-model="queryForm.routingCode" placeholder="请输入工艺路线编码" allow-clear @search="search" />
<a-input-search v-model="queryForm.routingName" placeholder="请输入工艺路线名称" allow-clear @search="search" />
<a-input-search v-model="queryForm.version" placeholder="请输入版本号" allow-clear @search="search" />
<a-input-search v-model="queryForm.isActive" placeholder="请输入是否启用" allow-clear @search="search" />
<a-input-search v-model="queryForm.status" placeholder="请输入状态" allow-clear @search="search" />
<a-button @click="reset">
<template #icon><icon-refresh /></template>
<template #default>重置</template>
</a-button>
</template>
<template #toolbar-right>
<a-button v-permission="['routing:routing:add']" type="primary" @click="onAdd">
<template #icon><icon-plus /></template>
<template #default>新增</template>
</a-button>
<a-button v-permission="['routing:routing:export']" @click="onExport">
<template #icon><icon-download /></template>
<template #default>导出</template>
</a-button>
</template>
<template #isActive="{ record }">
<GiCellTag :value="record.isActive" :dict="yse_or_no" />
</template>
<template #status="{ record }">
<GiCellTag :value="record.status" :dict="process_status" />
</template>
<template #action="{ record }">
<a-space>
<a-link v-permission="['routing:routing:detail']" title="详情" @click="onDetail(record)">详情</a-link>
<a-link v-permission="['routing:routing:update']" title="修改" @click="onUpdate(record)">修改</a-link>
<a-link
v-permission="['routing:routing:delete']"
status="danger"
:disabled="record.disabled"
:title="record.disabled ? '不可删除' : '删除'"
@click="onDelete(record)"
>
删除
</a-link>
</a-space>
</template>
</GiTable>
<RoutingAddModal ref="RoutingAddModalRef" @save-success="search" />
<RoutingDetailDrawer ref="RoutingDetailDrawerRef" />
</div>
</template>
<script setup lang="ts">
import RoutingAddModal from './RoutingAddModal.vue'
import RoutingDetailDrawer from './RoutingDetailDrawer.vue'
import { type RoutingResp, type RoutingQuery, deleteRouting, exportRouting, listRouting } from '@/apis/routing/routing'
import type { TableInstanceColumns } from '@/components/GiTable/type'
import { useDownload, useTable } from '@/hooks'
import { isMobile } from '@/utils'
import has from '@/utils/has'
import { useDict } from "@/hooks/app";
import GiCellTag from '@/components/GiCell/GiCellTag.vue'
defineOptions({ name: 'Routing' })
const { yse_or_no, process_status } = useDict('yse_or_no', 'process_status')
const queryForm = reactive<RoutingQuery>({
routingCode: undefined,
routingName: undefined,
version: undefined,
isActive: undefined,
status: undefined,
sort: ['id,desc']
})
const {
tableData: dataList,
loading,
pagination,
search,
handleDelete
} = useTable((page) => listRouting({ ...queryForm, ...page }), { immediate: true })
const columns = ref<TableInstanceColumns[]>([
{ title: '工艺路线编码', dataIndex: 'routingCode', slotName: 'routingCode' },
{ title: '工艺路线名称', dataIndex: 'routingName', slotName: 'routingName' },
{ title: '产品', dataIndex: 'productId', slotName: 'productId' },
{ title: '产线', dataIndex: 'lineId', slotName: 'lineId' },
{ title: '版本号', dataIndex: 'version', slotName: 'version' },
{ title: '是否启用', dataIndex: 'isActive', slotName: 'isActive', align: 'center' },
{ title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' },
{ title: '备注', dataIndex: 'remark', slotName: 'remark' },
{ title: '创建人', dataIndex: 'createUserString', slotName: 'createUser', show: false },
{ title: '创建时间', dataIndex: 'createTime', slotName: 'createTime', show: false },
{ title: '修改人', dataIndex: 'updateUserString', slotName: 'updateUser', show: false },
{ title: '修改时间', dataIndex: 'updateTime', slotName: 'updateTime', show: false },
{
title: '操作',
dataIndex: 'action',
slotName: 'action',
width: 160,
align: 'center',
fixed: !isMobile() ? 'right' : undefined,
show: has.hasPermOr(['routing:routing:detail', 'routing:routing:update', 'routing:routing:delete'])
}
]);
// 重置
const reset = () => {
queryForm.routingCode = undefined
queryForm.routingName = undefined
queryForm.version = undefined
queryForm.isActive = undefined
queryForm.status = undefined
search()
}
// 删除
const onDelete = (record: RoutingResp) => {
return handleDelete(() => deleteRouting(record.id), {
content: `是否确定删除该条数据?`,
showModal: true
})
}
// 导出
const onExport = () => {
useDownload(() => exportRouting(queryForm))
}
const RoutingAddModalRef = ref<InstanceType<typeof RoutingAddModal>>()
// 新增
const onAdd = () => {
RoutingAddModalRef.value?.onAdd()
}
// 修改
const onUpdate = (record: RoutingResp) => {
RoutingAddModalRef.value?.onUpdate(record.id)
}
const RoutingDetailDrawerRef = ref<InstanceType<typeof RoutingDetailDrawer>>()
// 详情
const onDetail = (record: RoutingResp) => {
RoutingDetailDrawerRef.value?.onOpen(record.id)
}
</script>
<style scoped lang="scss"></style>