feat(safety): 安全生产三期 - 隐患排查治理前端
配套后端隐患接口。
新增:
- api/safety/hazard
- views/safety/hazard:隐患列表 + 登记表单 + 提交整改弹窗 + 验收弹窗
* 列表按状态动态显示可用操作:待整改/整改中显示「整改」,待验收显示「验收」,
已闭环隐藏编辑
* 整改期限超期用红色加粗 + 「超期」标签提示
* 验收弹窗通过/驳回二选一,选驳回时必须填原因(前端先拦一道)
- dict-enum.ts 补 3 个隐患字典常量
注:本期只提交代码,未部署。
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { PageParam, PageResult } from '#/packages/effects/request/src'; |
| | | |
| | | import { requestClient } from '#/api/request'; |
| | | |
| | | export namespace MesSafetyHazardApi { |
| | | /** 鿣 */ |
| | | export interface Hazard { |
| | | id?: number; |
| | | code?: string; |
| | | name?: string; |
| | | source?: number; |
| | | hazardLevel?: number; |
| | | category?: number; |
| | | unitId?: number; |
| | | unitName?: string; |
| | | riskId?: number; |
| | | riskName?: string; |
| | | workshopId?: number; |
| | | workshopName?: string; |
| | | location?: string; |
| | | description?: string; |
| | | foundDate?: string; |
| | | founderUserId?: number; |
| | | founderUserName?: string; |
| | | chargeUserId?: number; |
| | | chargeUserName?: string; |
| | | deadline?: string; |
| | | rectifyRequirement?: string; |
| | | status?: number; |
| | | overdue?: boolean; |
| | | measure?: string; |
| | | finishDate?: string; |
| | | checkUserId?: number; |
| | | checkUserName?: string; |
| | | checkTime?: string; |
| | | checkResult?: string; |
| | | rejectReason?: string; |
| | | remark?: string; |
| | | createTime?: string; |
| | | } |
| | | |
| | | /** å页忰 */ |
| | | export interface HazardPageParam extends PageParam { |
| | | code?: string; |
| | | name?: string; |
| | | source?: number; |
| | | hazardLevel?: number; |
| | | status?: number; |
| | | workshopId?: number; |
| | | chargeUserId?: number; |
| | | riskId?: number; |
| | | foundDateStart?: string; |
| | | foundDateEnd?: string; |
| | | } |
| | | |
| | | /** æäº¤æ´æ¹åæ° */ |
| | | export interface HazardRectifyParam { |
| | | finishDate?: string; |
| | | id: number; |
| | | measure: string; |
| | | } |
| | | |
| | | /** éªæ¶åæ° */ |
| | | export interface HazardCheckParam { |
| | | checkResult?: string; |
| | | id: number; |
| | | pass: boolean; |
| | | rejectReason?: string; |
| | | } |
| | | } |
| | | |
| | | /** æ¥è¯¢éæ£å页 */ |
| | | export function getHazardPage(params: MesSafetyHazardApi.HazardPageParam) { |
| | | return requestClient.get<PageResult<MesSafetyHazardApi.Hazard>>( |
| | | '/mes/safety/hazard/page', |
| | | { params }, |
| | | ); |
| | | } |
| | | |
| | | /** æ¥è¯¢éæ£è¯¦æ
*/ |
| | | export function getHazard(id: number) { |
| | | return requestClient.get<MesSafetyHazardApi.Hazard>( |
| | | `/mes/safety/hazard/get?id=${id}`, |
| | | ); |
| | | } |
| | | |
| | | /** ç»è®°éæ£ */ |
| | | export function createHazard(data: MesSafetyHazardApi.Hazard) { |
| | | return requestClient.post<number>('/mes/safety/hazard/create', data); |
| | | } |
| | | |
| | | /** æ´æ°éæ£ */ |
| | | export function updateHazard(data: MesSafetyHazardApi.Hazard) { |
| | | return requestClient.put<boolean>('/mes/safety/hazard/update', data); |
| | | } |
| | | |
| | | /** å é¤éæ£ */ |
| | | export function deleteHazard(id: number) { |
| | | return requestClient.delete<boolean>(`/mes/safety/hazard/delete?id=${id}`); |
| | | } |
| | | |
| | | /** æäº¤æ´æ¹ */ |
| | | export function rectifyHazard(data: MesSafetyHazardApi.HazardRectifyParam) { |
| | | return requestClient.put<boolean>('/mes/safety/hazard/rectify', data); |
| | | } |
| | | |
| | | /** æ´æ¹éªæ¶ */ |
| | | export function checkHazard(data: MesSafetyHazardApi.HazardCheckParam) { |
| | | return requestClient.put<boolean>('/mes/safety/hazard/check', data); |
| | | } |
| | |
| | | MES_SAFETY_RISK_CATEGORY: 'mes_safety_risk_category', // å®å
¨ç产-é£é©ç±»å« |
| | | MES_SAFETY_ACCIDENT_TYPE: 'mes_safety_accident_type', // å®å
¨ç产-äºæ
ç±»å |
| | | MES_SAFETY_UNIT_STATUS: 'mes_safety_unit_status', // å®å
¨ç产-é£é©åå
ç¶æ |
| | | MES_SAFETY_HAZARD_SOURCE: 'mes_safety_hazard_source', // å®å
¨ç产-éæ£æ¥æº |
| | | MES_SAFETY_HAZARD_LEVEL: 'mes_safety_hazard_level', // å®å
¨ç产-鿣ç级 |
| | | MES_SAFETY_HAZARD_STATUS: 'mes_safety_hazard_status', // å®å
¨ç产-éæ£ç¶æ |
| | | } as const; |
| | | |
| | | /** ========== WMS - ä»å¨ç®¡ç模å ========== */ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { VbenFormSchema } from '#/adapter/form'; |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesSafetyHazardApi } from '#/api/safety/hazard'; |
| | | |
| | | import { DICT_TYPE } from '#/packages/constants/src'; |
| | | import { getDictOptions } from '#/packages/effects/hooks/src'; |
| | | |
| | | import { getRiskUnitSimpleList } from '#/api/safety/risk/unit'; |
| | | import { getSimpleUserList, getWorkshopOptions } from '../risk/shared'; |
| | | |
| | | /** éæ£ç¶æå¸¸é */ |
| | | export const HAZARD_STATUS = { |
| | | PENDING: 0, |
| | | RECTIFYING: 10, |
| | | TO_CHECK: 20, |
| | | CLOSED: 30, |
| | | }; |
| | | |
| | | /** ç¶æåç§° */ |
| | | export function hazardStatusName(status?: number): string { |
| | | switch (status) { |
| | | case 0: return 'å¾
æ´æ¹'; |
| | | case 10: return 'æ´æ¹ä¸'; |
| | | case 20: return 'å¾
éªæ¶'; |
| | | case 30: return 'å·²éç¯'; |
| | | default: return '-'; |
| | | } |
| | | } |
| | | |
| | | /** ç¶æé¢è² */ |
| | | export function hazardStatusColor(status?: number): string { |
| | | switch (status) { |
| | | case 0: return 'error'; |
| | | case 10: return 'processing'; |
| | | case 20: return 'warning'; |
| | | case 30: return 'success'; |
| | | default: return 'default'; |
| | | } |
| | | } |
| | | |
| | | /** å表çæç´¢è¡¨å */ |
| | | export function useGridFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'code', |
| | | label: '鿣ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { allowClear: true, placeholder: '请è¾å
¥éæ£ç¼ç ' }, |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: '鿣åç§°', |
| | | component: 'Input', |
| | | componentProps: { allowClear: true, placeholder: '请è¾å
¥éæ£åç§°' }, |
| | | }, |
| | | { |
| | | fieldName: 'hazardLevel', |
| | | label: '鿣ç级', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_SAFETY_HAZARD_LEVEL, 'number'), |
| | | placeholder: 'è¯·éæ©éæ£ç级', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'status', |
| | | label: 'ç¶æ', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_SAFETY_HAZARD_STATUS, 'number'), |
| | | placeholder: 'è¯·éæ©ç¶æ', |
| | | }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** ç»è®°/ä¿®æ¹ç表å */ |
| | | export function useFormSchema(): VbenFormSchema[] { |
| | | return [ |
| | | { |
| | | fieldName: 'id', |
| | | component: 'Input', |
| | | dependencies: { triggerFields: [''], show: () => false }, |
| | | }, |
| | | { |
| | | fieldName: 'code', |
| | | label: '鿣ç¼ç ', |
| | | component: 'Input', |
| | | componentProps: { placeholder: 'å¦ YH-2026-001' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'name', |
| | | label: '鿣åç§°', |
| | | component: 'Input', |
| | | componentProps: { placeholder: 'å¦ é
çµç®±ç¼ºé²æ¤ç½©' }, |
| | | rules: 'required', |
| | | }, |
| | | { |
| | | fieldName: 'source', |
| | | label: 'éæ£æ¥æº', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_SAFETY_HAZARD_SOURCE, 'number'), |
| | | placeholder: 'è¯·éæ©éæ£æ¥æº', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'hazardLevel', |
| | | label: '鿣ç级', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_SAFETY_HAZARD_LEVEL, 'number'), |
| | | placeholder: 'è¯·éæ©éæ£ç级', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'category', |
| | | label: 'éæ£ç±»å«', |
| | | component: 'Select', |
| | | componentProps: { |
| | | allowClear: true, |
| | | options: getDictOptions(DICT_TYPE.MES_SAFETY_RISK_CATEGORY, 'number'), |
| | | placeholder: 'è¯·éæ©éæ£ç±»å«', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'unitId', |
| | | label: 'å
³èé£é©åå
', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | api: getRiskUnitSimpleList, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'å¯å
³èå°å
·ä½é£é©ç¹', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'workshopId', |
| | | label: 'æå±è½¦é´', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | api: getWorkshopOptions, |
| | | labelField: 'name', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©æå±è½¦é´', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'location', |
| | | label: 'å
·ä½ä½ç½®', |
| | | component: 'Input', |
| | | componentProps: { placeholder: 'å¦ é 纸车é´é
çµæ¿' }, |
| | | }, |
| | | { |
| | | fieldName: 'description', |
| | | label: '鿣æè¿°', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { placeholder: '请æè¿°éæ£çå
·ä½æ
åµ', rows: 3 }, |
| | | }, |
| | | { |
| | | fieldName: 'foundDate', |
| | | label: 'åç°æ¥æ', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: 'è¯·éæ©åç°æ¥æ', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'founderUserId', |
| | | label: 'åç°äºº', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | allowClear: true, |
| | | api: getSimpleUserList, |
| | | labelField: 'nickname', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©åç°äºº', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'chargeUserId', |
| | | label: 'æ´æ¹è´£ä»»äºº', |
| | | component: 'ApiSelect', |
| | | componentProps: { |
| | | api: getSimpleUserList, |
| | | labelField: 'nickname', |
| | | valueField: 'id', |
| | | placeholder: 'è¯·éæ©æ´æ¹è´£ä»»äºº', |
| | | }, |
| | | rules: 'selectRequired', |
| | | }, |
| | | { |
| | | fieldName: 'deadline', |
| | | label: 'æ´æ¹æé', |
| | | component: 'DatePicker', |
| | | componentProps: { |
| | | class: '!w-full', |
| | | format: 'YYYY-MM-DD', |
| | | valueFormat: 'YYYY-MM-DD', |
| | | placeholder: 'è¯·éæ©æ´æ¹æé', |
| | | }, |
| | | }, |
| | | { |
| | | fieldName: 'rectifyRequirement', |
| | | label: 'æ´æ¹è¦æ±', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { placeholder: '请è¾å
¥æ´æ¹è¦æ±', rows: 2 }, |
| | | }, |
| | | { |
| | | fieldName: 'remark', |
| | | label: '夿³¨', |
| | | component: 'Textarea', |
| | | formItemClass: 'col-span-2', |
| | | componentProps: { placeholder: '请è¾å
¥å¤æ³¨', rows: 2 }, |
| | | }, |
| | | ]; |
| | | } |
| | | |
| | | /** å表çåæ®µ */ |
| | | export function useGridColumns(): VxeTableGridOptions<MesSafetyHazardApi.Hazard>['columns'] { |
| | | return [ |
| | | { field: 'code', title: '鿣ç¼ç ', width: 140 }, |
| | | { field: 'name', title: '鿣åç§°', minWidth: 180 }, |
| | | { |
| | | field: 'hazardLevel', |
| | | title: 'ç级', |
| | | width: 100, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_SAFETY_HAZARD_LEVEL }, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'source', |
| | | title: 'æ¥æº', |
| | | width: 110, |
| | | cellRender: { |
| | | name: 'CellDict', |
| | | props: { type: DICT_TYPE.MES_SAFETY_HAZARD_SOURCE }, |
| | | }, |
| | | }, |
| | | { field: 'workshopName', title: '车é´', width: 110 }, |
| | | { field: 'location', title: 'ä½ç½®', minWidth: 140 }, |
| | | { field: 'chargeUserName', title: 'æ´æ¹è´£ä»»äºº', width: 110 }, |
| | | { field: 'deadline', title: 'æ´æ¹æé', width: 120, slots: { default: 'deadline' } }, |
| | | { |
| | | field: 'status', |
| | | title: 'ç¶æ', |
| | | width: 100, |
| | | slots: { default: 'status' }, |
| | | }, |
| | | { |
| | | title: 'æä½', |
| | | width: 240, |
| | | fixed: 'right', |
| | | slots: { default: 'actions' }, |
| | | }, |
| | | ]; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { VxeTableGridOptions } from '#/adapter/vxe-table'; |
| | | import type { MesSafetyHazardApi } from '#/api/safety/hazard'; |
| | | |
| | | import { Page } from '#/packages/effects/common-ui/src'; |
| | | |
| | | import { message, Tag } from 'ant-design-vue'; |
| | | |
| | | import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; |
| | | import { deleteHazard, getHazardPage } from '#/api/safety/hazard'; |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { |
| | | HAZARD_STATUS, |
| | | hazardStatusColor, |
| | | hazardStatusName, |
| | | useGridColumns, |
| | | useGridFormSchema, |
| | | } from './data'; |
| | | import CheckModal from './modules/check.vue'; |
| | | import FormModalComp from './modules/form.vue'; |
| | | import RectifyModal from './modules/rectify.vue'; |
| | | |
| | | defineOptions({ name: 'SafetyHazard' }); |
| | | |
| | | const [FormModal, formModalApi] = useVbenModal({ |
| | | connectedComponent: FormModalComp, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [RectifyModalComp, rectifyModalApi] = useVbenModal({ |
| | | connectedComponent: RectifyModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [CheckModalComp, checkModalApi] = useVbenModal({ |
| | | connectedComponent: CheckModal, |
| | | destroyOnClose: true, |
| | | }); |
| | | |
| | | const [Grid, gridApi] = useVbenVxeGrid({ |
| | | formOptions: { |
| | | schema: useGridFormSchema(), |
| | | }, |
| | | gridOptions: { |
| | | columns: useGridColumns(), |
| | | height: 'auto', |
| | | keepSource: true, |
| | | proxyConfig: { |
| | | ajax: { |
| | | query: async ({ page }, formValues) => |
| | | await getHazardPage({ |
| | | pageNo: page.currentPage, |
| | | pageSize: page.pageSize, |
| | | ...formValues, |
| | | }), |
| | | }, |
| | | }, |
| | | rowConfig: { |
| | | keyField: 'id', |
| | | isHover: true, |
| | | }, |
| | | toolbarConfig: { |
| | | refresh: true, |
| | | search: true, |
| | | }, |
| | | } as VxeTableGridOptions<MesSafetyHazardApi.Hazard>, |
| | | }); |
| | | |
| | | function handleCreate() { |
| | | formModalApi.setData({ formType: 'create' }).open(); |
| | | } |
| | | |
| | | function handleEdit(row: MesSafetyHazardApi.Hazard) { |
| | | formModalApi.setData({ formType: 'update', id: row.id }).open(); |
| | | } |
| | | |
| | | function handleRectify(row: MesSafetyHazardApi.Hazard) { |
| | | rectifyModalApi.setData({ id: row.id, name: row.name }).open(); |
| | | } |
| | | |
| | | function handleCheck(row: MesSafetyHazardApi.Hazard) { |
| | | checkModalApi |
| | | .setData({ id: row.id, name: row.name, measure: row.measure }) |
| | | .open(); |
| | | } |
| | | |
| | | function handleRefresh() { |
| | | gridApi.query(); |
| | | } |
| | | |
| | | async function handleDelete(row: MesSafetyHazardApi.Hazard) { |
| | | await deleteHazard(row.id!); |
| | | message.success('å 餿å'); |
| | | gridApi.query(); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Page auto-content-height> |
| | | <FormModal @success="handleRefresh" /> |
| | | <RectifyModalComp @success="handleRefresh" /> |
| | | <CheckModalComp @success="handleRefresh" /> |
| | | <Grid> |
| | | <template #toolbar-tools> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'ç»è®°éæ£', |
| | | type: 'primary', |
| | | icon: ACTION_ICON.ADD, |
| | | auth: ['mes:safety-hazard:create'], |
| | | onClick: handleCreate, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | |
| | | <template #deadline="{ row }"> |
| | | <span v-if="row.deadline" :class="row.overdue ? 'font-semibold text-red-600' : ''"> |
| | | {{ row.deadline }} |
| | | <Tag v-if="row.overdue" color="error" class="ml-1">è¶
æ</Tag> |
| | | </span> |
| | | <span v-else class="text-gray-400">-</span> |
| | | </template> |
| | | |
| | | <template #status="{ row }"> |
| | | <Tag :color="hazardStatusColor(row.status)"> |
| | | {{ hazardStatusName(row.status) }} |
| | | </Tag> |
| | | </template> |
| | | |
| | | <template #actions="{ row }"> |
| | | <TableAction |
| | | :actions="[ |
| | | { |
| | | label: 'æ´æ¹', |
| | | type: 'link', |
| | | auth: ['mes:safety-hazard:rectify'], |
| | | ifShow: |
| | | row.status === HAZARD_STATUS.PENDING || |
| | | row.status === HAZARD_STATUS.RECTIFYING, |
| | | onClick: handleRectify.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'éªæ¶', |
| | | type: 'link', |
| | | auth: ['mes:safety-hazard:check'], |
| | | ifShow: row.status === HAZARD_STATUS.TO_CHECK, |
| | | onClick: handleCheck.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'ç¼è¾', |
| | | type: 'link', |
| | | icon: ACTION_ICON.EDIT, |
| | | auth: ['mes:safety-hazard:update'], |
| | | ifShow: row.status !== HAZARD_STATUS.CLOSED, |
| | | onClick: handleEdit.bind(null, row), |
| | | }, |
| | | { |
| | | label: 'å é¤', |
| | | type: 'link', |
| | | danger: true, |
| | | icon: ACTION_ICON.DELETE, |
| | | auth: ['mes:safety-hazard:delete'], |
| | | popConfirm: { |
| | | title: 'ç¡®å®å é¤è¯¥éæ£è®°å½åï¼', |
| | | confirm: handleDelete.bind(null, row), |
| | | }, |
| | | }, |
| | | ]" |
| | | /> |
| | | </template> |
| | | </Grid> |
| | | </Page> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { Input, message, Radio } from 'ant-design-vue'; |
| | | |
| | | import { checkHazard } from '#/api/safety/hazard'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const hazardId = ref<number>(); |
| | | const hazardName = ref<string>(); |
| | | const measure = ref<string>(); |
| | | const passed = ref<boolean>(true); |
| | | const checkResult = ref<string>(''); |
| | | const rejectReason = ref<string>(''); |
| | | const loading = ref(false); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!passed.value && !rejectReason.value.trim()) { |
| | | message.warning('éªæ¶ä¸éè¿æ¶å¿
须填å驳ååå '); |
| | | return; |
| | | } |
| | | loading.value = true; |
| | | try { |
| | | await checkHazard({ |
| | | id: hazardId.value!, |
| | | pass: passed.value, |
| | | checkResult: checkResult.value, |
| | | rejectReason: rejectReason.value, |
| | | }); |
| | | emit('success'); |
| | | message.success(passed.value ? 'éªæ¶éè¿ï¼éæ£å·²éç¯' : '已驳åï¼éåæ´æ¹'); |
| | | await modalApi.close(); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }, |
| | | onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ |
| | | id: number; |
| | | measure?: string; |
| | | name?: string; |
| | | }>(); |
| | | hazardId.value = data.id; |
| | | hazardName.value = data.name; |
| | | measure.value = data.measure; |
| | | passed.value = true; |
| | | checkResult.value = ''; |
| | | rejectReason.value = ''; |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="æ´æ¹éªæ¶" class="w-1/2"> |
| | | <div class="mx-4 flex flex-col gap-4"> |
| | | <div> |
| | | <div class="mb-1 text-gray-600">鿣</div> |
| | | <div class="font-medium">{{ hazardName ?? '-' }}</div> |
| | | </div> |
| | | <div> |
| | | <div class="mb-1 text-gray-600">æ´æ¹æªæ½ï¼æäº¤å
容ï¼</div> |
| | | <div class="rounded bg-gray-50 p-2">{{ measure || '-' }}</div> |
| | | </div> |
| | | <div> |
| | | <div class="mb-1 text-gray-600">éªæ¶ç»è®º</div> |
| | | <Radio.Group v-model:value="passed" button-style="solid"> |
| | | <Radio.Button :value="true">éè¿å¹¶éç¯</Radio.Button> |
| | | <Radio.Button :value="false">驳åéæ°æ´æ¹</Radio.Button> |
| | | </Radio.Group> |
| | | </div> |
| | | <div v-if="passed"> |
| | | <div class="mb-1 text-gray-600">éªæ¶æè§</div> |
| | | <Input.TextArea |
| | | v-model:value="checkResult" |
| | | :rows="2" |
| | | placeholder="å¦ï¼å·²æ´æ¹å°ä½" |
| | | /> |
| | | </div> |
| | | <div v-else> |
| | | <div class="mb-1 text-gray-600"> |
| | | 驳ååå <span class="text-red-500">*</span> |
| | | </div> |
| | | <Input.TextArea |
| | | v-model:value="rejectReason" |
| | | :rows="2" |
| | | placeholder="å¦ï¼é²æ¤ç½©åºå®ä¸ç¢ï¼ééæ°å®è£
" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import type { MesSafetyHazardApi } from '#/api/safety/hazard'; |
| | | |
| | | import { computed, ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { message } from 'ant-design-vue'; |
| | | |
| | | import { useVbenForm } from '#/adapter/form'; |
| | | import { |
| | | createHazard, |
| | | getHazard, |
| | | updateHazard, |
| | | } from '#/api/safety/hazard'; |
| | | |
| | | import { useFormSchema } from '../data'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const formType = ref<'create' | 'update'>('create'); |
| | | const isUpdate = computed(() => formType.value === 'update'); |
| | | const getTitle = computed(() => |
| | | isUpdate.value ? 'ä¿®æ¹éæ£' : 'ç»è®°éæ£', |
| | | ); |
| | | |
| | | const [Form, formApi] = useVbenForm({ |
| | | commonConfig: { |
| | | componentProps: { class: 'w-full' }, |
| | | formItemClass: 'col-span-1', |
| | | labelWidth: 110, |
| | | }, |
| | | wrapperClass: 'grid-cols-2', |
| | | layout: 'horizontal', |
| | | schema: useFormSchema(), |
| | | showDefaultActions: false, |
| | | }); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | const { valid } = await formApi.validate(); |
| | | if (!valid) { |
| | | return; |
| | | } |
| | | modalApi.lock(); |
| | | const data = (await formApi.getValues()) as MesSafetyHazardApi.Hazard; |
| | | try { |
| | | await (isUpdate.value ? updateHazard(data) : createHazard(data)); |
| | | emit('success'); |
| | | message.success('æä½æå'); |
| | | await modalApi.close(); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | }, |
| | | async onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ |
| | | formType: 'create' | 'update'; |
| | | id?: number; |
| | | }>(); |
| | | formType.value = data.formType; |
| | | await formApi.resetForm(); |
| | | if (data?.id) { |
| | | modalApi.lock(); |
| | | try { |
| | | const hazard = await getHazard(data.id); |
| | | await formApi.setValues(hazard); |
| | | } finally { |
| | | modalApi.unlock(); |
| | | } |
| | | } |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal :title="getTitle" class="w-2/3"> |
| | | <Form class="mx-4" /> |
| | | </Modal> |
| | | </template> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <script lang="ts" setup> |
| | | import { ref } from 'vue'; |
| | | |
| | | import { useVbenModal } from '@vben/common-ui'; |
| | | |
| | | import { DatePicker, Input, message } from 'ant-design-vue'; |
| | | |
| | | import { rectifyHazard } from '#/api/safety/hazard'; |
| | | |
| | | const emit = defineEmits(['success']); |
| | | |
| | | const hazardId = ref<number>(); |
| | | const hazardName = ref<string>(); |
| | | const measure = ref<string>(''); |
| | | const finishDate = ref<string>(); |
| | | const loading = ref(false); |
| | | |
| | | const [Modal, modalApi] = useVbenModal({ |
| | | async onConfirm() { |
| | | if (!measure.value || measure.value.trim().length === 0) { |
| | | message.warning('è¯·å¡«åæ´æ¹æªæ½'); |
| | | return; |
| | | } |
| | | loading.value = true; |
| | | try { |
| | | await rectifyHazard({ |
| | | id: hazardId.value!, |
| | | measure: measure.value, |
| | | finishDate: finishDate.value, |
| | | }); |
| | | emit('success'); |
| | | message.success('å·²æäº¤æ´æ¹ï¼çå¾
éªæ¶'); |
| | | await modalApi.close(); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }, |
| | | onOpenChange(isOpen: boolean) { |
| | | if (!isOpen) { |
| | | return; |
| | | } |
| | | const data = modalApi.getData<{ id: number; name?: string }>(); |
| | | hazardId.value = data.id; |
| | | hazardName.value = data.name; |
| | | measure.value = ''; |
| | | finishDate.value = undefined; |
| | | }, |
| | | }); |
| | | </script> |
| | | |
| | | <template> |
| | | <Modal title="æäº¤æ´æ¹" class="w-1/2"> |
| | | <div class="mx-4 flex flex-col gap-4"> |
| | | <div> |
| | | <div class="mb-1 text-gray-600">鿣</div> |
| | | <div class="font-medium">{{ hazardName ?? '-' }}</div> |
| | | </div> |
| | | <div> |
| | | <div class="mb-1 text-gray-600"> |
| | | æ´æ¹æªæ½ <span class="text-red-500">*</span> |
| | | </div> |
| | | <Input.TextArea |
| | | v-model:value="measure" |
| | | :rows="4" |
| | | placeholder="请填åå®é
éåçæ´æ¹æªæ½" |
| | | /> |
| | | </div> |
| | | <div> |
| | | <div class="mb-1 text-gray-600">æ´æ¹å®ææ¥æ</div> |
| | | <DatePicker |
| | | v-model:value="finishDate" |
| | | class="w-full" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | placeholder="ä¸å¡«åé»è®¤ä»å¤©" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </Modal> |
| | | </template> |