Commit 18656f01 by wangchunyang

日常管理框架代码

parent 083e4d98
import axios from '@/libs/api.request'
// 入库列表
export const getInboundList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmInbound/selectList',
method: 'post',
data: param
})
}
// 保存入库(含明细)
export const saveInbound = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmInbound/save',
method: 'post',
data: param
})
}
// 删除入库记录
export const deleteInbound = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmInbound/delete',
method: 'post',
data: param
})
}
// 执行入库(生成库存变更与日志)
export const doInbound = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmInbound/doInbound',
method: 'post',
data: param
})
}
// 待归还列表(从申领中查询)
export const getPendingReturnList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmInbound/selectPendingReturnList',
method: 'post',
data: param
})
}
// 处理归还(生成入库单并更新库存)
export const processReturn = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmInbound/processReturn',
method: 'post',
data: param
})
}
// 库存查询
export const getInventoryList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmInbound/selectInventoryList',
method: 'post',
data: param
})
}
// 详情
export const getInboundById = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmInbound/getById',
method: 'post',
data: param
})
}
import axios from '@/libs/api.request'
// 申领列表(包含历史与查询)
export const getBorrowList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmBorrow/selectList',
method: 'post',
data: param
})
}
// 待审核列表
export const getPendingBorrowList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmBorrow/selectPendingList',
method: 'post',
data: param
})
}
// 保存申请(含明细)
export const saveBorrowApplication = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmBorrow/save',
method: 'post',
data: param
})
}
// 提交
export const submitBorrow = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmBorrow/submit',
method: 'post',
data: param
})
}
// 撤回
export const revokeBorrow = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmBorrow/revoke',
method: 'post',
data: param
})
}
// 审批通过(内部生成出库、更新库存)
export const approveBorrow = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmBorrow/approve',
method: 'post',
data: param
})
}
// 驳回
export const rejectBorrow = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmBorrow/reject',
method: 'post',
data: param
})
}
// 查询申请及明细/日志
export const getBorrowById = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmBorrow/getById',
method: 'post',
data: param
})
}
import axios from '@/libs/api.request'
// 列表查询(支持分页与筛选)
export const getLeaveList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/selectList',
method: 'post',
data: param
})
}
// 获取待审核列表
export const getPendingList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/selectPendingList',
method: 'post',
data: param
})
}
// 保存(新增/修改)请假申请
export const saveLeaveApplication = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/save',
method: 'post',
data: param
})
}
// 提交请假申请进入审批
export const submitLeaveApplication = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/submit',
method: 'post',
data: param
})
}
// 撤回请假申请
export const revokeLeaveApplication = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/revoke',
method: 'post',
data: param
})
}
// 审批通过
export const approveLeave = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/approve',
method: 'post',
data: param
})
}
// 驳回
export const rejectLeave = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/reject',
method: 'post',
data: param
})
}
// 获取某申请的详情(含审批记录)
export const getLeaveById = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/getById',
method: 'post',
data: param
})
}
// 审批历史(用于历史查询)
export const getApprovalHistory = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/selectApprovalHistory',
method: 'post',
data: param
})
}
// 请假统计
export const getLeaveStats = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/selectStats',
method: 'post',
data: param
})
}
import axios from '@/libs/api.request'
// 使用统计
export const getUsageStats = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmStats/selectUsageStats',
method: 'post',
data: param
})
}
export const getUsageDetails = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmStats/selectUsageDetails',
method: 'post',
data: param
})
}
// 工作量统计
export const getWorkloadStats = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmStats/selectWorkloadStats',
method: 'post',
data: param
})
}
export const getWorkloadDetails = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmStats/selectWorkloadDetails',
method: 'post',
data: param
})
}
import axios from '@/libs/api.request'
// 获取日常人员列表
export const getDmUserList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUser/selectList',
method: 'post',
data: param
})
}
// 根据机构编码同步本级及下属机构用户(存在则更新、不存在则新增;null 不覆盖)
export const syncDmUsersByOffice = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUser/syncByOffice',
method: 'post',
data: param
})
}
// 保存单个日常人员(前端编辑后保存)
export const saveDmUser = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUser/save',
method: 'post',
data: param
})
}
import axios from '@/libs/api.request'
// ===== 人员分类管理 =====
export const getUserCategoryList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/selectList',
method: 'post',
data: param
})
}
export const saveUserCategory = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/save',
method: 'post',
data: param
})
}
export const deleteUserCategory = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/delete',
method: 'post',
data: param
})
}
// ===== 人员分类权限管理 =====
export const getUserCategoryPermissionList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategoryPermission/selectList',
method: 'post',
data: param
})
}
export const saveUserCategoryPermission = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategoryPermission/save',
method: 'post',
data: param
})
}
// ===== 请假类型管理 =====
export const getLeaveTypeList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeaveType/selectList',
method: 'post',
data: param
})
}
export const saveLeaveType = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeaveType/save',
method: 'post',
data: param
})
}
export const deleteLeaveType = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeaveType/delete',
method: 'post',
data: param
})
}
// ===== 办公用品分类管理 =====
export const getMaterialCategoryList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmMaterialCategory/selectList',
method: 'post',
data: param
})
}
export const saveMaterialCategory = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmMaterialCategory/save',
method: 'post',
data: param
})
}
export const deleteMaterialCategory = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmMaterialCategory/delete',
method: 'post',
data: param
})
}
// ===== 办公用品管理 =====
export const getMaterialList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmMaterial/selectList',
method: 'post',
data: param
})
}
export const saveMaterial = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmMaterial/save',
method: 'post',
data: param
})
}
export const deleteMaterial = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmMaterial/delete',
method: 'post',
data: param
})
}
<template>
<div class="key-dm-wrapper">
<Tabs v-model="activeTab" @on-click="handleTabChange">
<!-- 人员分类管理 -->
<TabPane label="人员分类管理" name="userCategory">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="6">
<span>分类名称:</span>
<Input v-model="filters.userCategory.catgory_name" placeholder="请输入" style="width: 70%" />
</Col>
<Col span="12" class="text-right">
<Button type="primary" class="mr10" @click="handleSearch('userCategory')">搜索</Button>
<Button class="mr10" @click="handleReset('userCategory')">重置</Button>
<Button type="success" @click="openAddModal('userCategory')">新增</Button>
</Col>
</Row>
</div>
<Table border :loading="loading.userCategory" :columns="userCategoryColumns" :data="tables.userCategory">
<template slot="action" slot-scope="{ row }">
<Button size="small" type="primary" class="mr5" @click="openEditModal('userCategory', row)">修改</Button>
<Button size="small" type="warning" class="mr5" @click="openPermissionModal(row)">权限配置</Button>
<Poptip confirm title="确认删除?" transfer @on-ok="handleDelete('userCategory', row)">
<Button size="small" type="error">删除</Button>
</Poptip>
</template>
</Table>
<Page
class="page_style"
:total="pagers.userCategory.totalRecord"
:current="pagers.userCategory.pageNo"
:page-size="pagers.userCategory.pageSize"
show-total
show-sizer
@on-change="pageChange('userCategory', $event)"
@on-page-size-change="sizeChange('userCategory', $event)"
/>
</TabPane>
<!-- 假种管理 -->
<TabPane label="假种管理" name="leaveType">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="6">
<span>类型名称:</span>
<Input v-model="filters.leaveType.type_name" placeholder="请输入" style="width: 70%" />
</Col>
<Col span="6">
<span>类型编码:</span>
<Input v-model="filters.leaveType.type_code" placeholder="请输入" style="width: 70%" />
</Col>
<Col span="12" class="text-right">
<Button type="primary" class="mr10" @click="handleSearch('leaveType')">搜索</Button>
<Button class="mr10" @click="handleReset('leaveType')">重置</Button>
<Button type="success" @click="openAddModal('leaveType')">新增</Button>
</Col>
</Row>
</div>
<Table border :loading="loading.leaveType" :columns="leaveTypeColumns" :data="tables.leaveType">
<template slot="action" slot-scope="{ row }">
<Button size="small" type="primary" class="mr5" @click="openEditModal('leaveType', row)">修改</Button>
<Poptip confirm title="确认删除?" transfer @on-ok="handleDelete('leaveType', row)">
<Button size="small" type="error">删除</Button>
</Poptip>
</template>
</Table>
<Page
class="page_style"
:total="pagers.leaveType.totalRecord"
:current="pagers.leaveType.pageNo"
:page-size="pagers.leaveType.pageSize"
show-total
show-sizer
@on-change="pageChange('leaveType', $event)"
@on-page-size-change="sizeChange('leaveType', $event)"
/>
</TabPane>
<!-- 办公用品分类管理 -->
<TabPane label="办公用品分类管理" name="materialCategory">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="6">
<span>分类名称:</span>
<Input v-model="filters.materialCategory.category_Name" placeholder="请输入" style="width: 70%" />
</Col>
<Col span="6">
<span>分类编码:</span>
<Input v-model="filters.materialCategory.category_Code" placeholder="请输入" style="width: 70%" />
</Col>
<Col span="12" class="text-right">
<Button type="primary" class="mr10" @click="handleSearch('materialCategory')">搜索</Button>
<Button class="mr10" @click="handleReset('materialCategory')">重置</Button>
<Button type="success" @click="openAddModal('materialCategory')">新增</Button>
</Col>
</Row>
</div>
<Table border :loading="loading.materialCategory" :columns="materialCategoryColumns" :data="tables.materialCategory">
<template slot="action" slot-scope="{ row }">
<Button size="small" type="primary" class="mr5" @click="openEditModal('materialCategory', row)">修改</Button>
<Poptip confirm title="确认删除?" transfer @on-ok="handleDelete('materialCategory', row)">
<Button size="small" type="error">删除</Button>
</Poptip>
</template>
</Table>
<Page
class="page_style"
:total="pagers.materialCategory.totalRecord"
:current="pagers.materialCategory.pageNo"
:page-size="pagers.materialCategory.pageSize"
show-total
show-sizer
@on-change="pageChange('materialCategory', $event)"
@on-page-size-change="sizeChange('materialCategory', $event)"
/>
</TabPane>
<!-- 办公用品管理 -->
<TabPane label="办公用品管理" name="material">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="6">
<span>物料名称:</span>
<Input v-model="filters.material.material_name" placeholder="请输入" style="width: 70%" />
</Col>
<Col span="6">
<span>物料编码:</span>
<Input v-model="filters.material.material_code" placeholder="请输入" style="width: 70%" />
</Col>
<Col span="6">
<span>分类:</span>
<Select v-model="filters.material.category_id" clearable style="width: 70%">
<Option v-for="cat in materialCategoryOptions" :key="cat.id" :value="cat.id">{{ cat.category_Name }}</Option>
</Select>
</Col>
<Col span="6" class="text-right">
<Button type="primary" class="mr10" @click="handleSearch('material')">搜索</Button>
<Button class="mr10" @click="handleReset('material')">重置</Button>
<Button type="success" @click="openAddModal('material')">新增</Button>
</Col>
</Row>
</div>
<Table border :loading="loading.material" :columns="materialColumns" :data="tables.material">
<template slot="action" slot-scope="{ row }">
<Button size="small" type="primary" class="mr5" @click="openEditModal('material', row)">修改</Button>
<Poptip confirm title="确认删除?" transfer @on-ok="handleDelete('material', row)">
<Button size="small" type="error">删除</Button>
</Poptip>
</template>
</Table>
<Page
class="page_style"
:total="pagers.material.totalRecord"
:current="pagers.material.pageNo"
:page-size="pagers.material.pageSize"
show-total
show-sizer
@on-change="pageChange('material', $event)"
@on-page-size-change="sizeChange('material', $event)"
/>
</TabPane>
</Tabs>
<!-- 人员分类管理弹窗 -->
<Modal v-model="modals.userCategory.visible" :title="modals.userCategory.isEdit ? '修改人员分类' : '新增人员分类'" width="600" :mask-closable="false">
<Form :label-width="120" :model="modals.userCategory.form" :rules="modals.userCategory.rules" ref="userCategoryForm">
<FormItem label="分类名称" prop="catgory_name">
<Input v-model="modals.userCategory.form.catgory_name" placeholder="请输入分类名称" />
</FormItem>
<FormItem label="描述" prop="remark">
<Input type="textarea" :rows="3" v-model="modals.userCategory.form.remark" placeholder="请输入描述" />
</FormItem>
<FormItem label="排序号" prop="order_no">
<InputNumber v-model="modals.userCategory.form.order_no" :min="0" style="width: 100%" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="modals.userCategory.visible = false">取消</Button>
<Button type="primary" :loading="modals.userCategory.saving" @click="handleSave('userCategory')">保存</Button>
</div>
</Modal>
<!-- 假种管理弹窗 -->
<Modal v-model="modals.leaveType.visible" :title="modals.leaveType.isEdit ? '修改请假类型' : '新增请假类型'" width="700" :mask-closable="false">
<Form :label-width="120" :model="modals.leaveType.form" :rules="modals.leaveType.rules" ref="leaveTypeForm">
<Row :gutter="16">
<Col span="12">
<FormItem label="类型编码" prop="type_code">
<Input v-model="modals.leaveType.form.type_code" placeholder="请输入类型编码" />
</FormItem>
</Col>
<Col span="12">
<FormItem label="类型名称" prop="type_name">
<Input v-model="modals.leaveType.form.type_name" placeholder="请输入类型名称" />
</FormItem>
</Col>
</Row>
<FormItem label="类型描述" prop="remark">
<Input type="textarea" :rows="3" v-model="modals.leaveType.form.remark" placeholder="请输入类型描述" />
</FormItem>
<Row :gutter="16">
<Col span="12">
<FormItem label="基准天数" prop="base_days">
<InputNumber v-model="modals.leaveType.form.base_days" :min="0" :precision="2" style="width: 100%" />
</FormItem>
</Col>
<Col span="12">
<FormItem label="年度上限" prop="max_days_per_year">
<InputNumber v-model="modals.leaveType.form.max_days_per_year" :min="0" :precision="2" style="width: 100%" />
</FormItem>
</Col>
</Row>
<Row :gutter="16">
<Col span="12">
<FormItem label="需要审批">
<Select v-model="modals.leaveType.form.need_approval" style="width: 100%">
<Option :value="1">需要</Option>
<Option :value="0">不需要</Option>
</Select>
</FormItem>
</Col>
<Col span="12">
<FormItem label="排序号" prop="order_no">
<InputNumber v-model="modals.leaveType.form.order_no" :min="0" style="width: 100%" />
</FormItem>
</Col>
</Row>
</Form>
<div slot="footer">
<Button @click="modals.leaveType.visible = false">取消</Button>
<Button type="primary" :loading="modals.leaveType.saving" @click="handleSave('leaveType')">保存</Button>
</div>
</Modal>
<!-- 办公用品分类管理弹窗 -->
<Modal v-model="modals.materialCategory.visible" :title="modals.materialCategory.isEdit ? '修改办公用品分类' : '新增办公用品分类'" width="600" :mask-closable="false">
<Form :label-width="120" :model="modals.materialCategory.form" :rules="modals.materialCategory.rules" ref="materialCategoryForm">
<FormItem label="分类编码" prop="category_Code">
<Input v-model="modals.materialCategory.form.category_Code" placeholder="请输入分类编码" />
</FormItem>
<FormItem label="分类名称" prop="category_Name">
<Input v-model="modals.materialCategory.form.category_Name" placeholder="请输入分类名称" />
</FormItem>
<FormItem label="分类描述" prop="remark">
<Input type="textarea" :rows="3" v-model="modals.materialCategory.form.remark" placeholder="请输入分类描述" />
</FormItem>
<FormItem label="排序号" prop="order_no">
<InputNumber v-model="modals.materialCategory.form.order_no" :min="0" style="width: 100%" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="modals.materialCategory.visible = false">取消</Button>
<Button type="primary" :loading="modals.materialCategory.saving" @click="handleSave('materialCategory')">保存</Button>
</div>
</Modal>
<!-- 办公用品管理弹窗 -->
<Modal v-model="modals.material.visible" :title="modals.material.isEdit ? '修改办公用品' : '新增办公用品'" width="800" :mask-closable="false">
<Form :label-width="120" :model="modals.material.form" :rules="modals.material.rules" ref="materialForm">
<Row :gutter="16">
<Col span="12">
<FormItem label="分类" prop="category_id">
<Select v-model="modals.material.form.category_id" placeholder="请选择分类" style="width: 100%">
<Option v-for="cat in materialCategoryOptions" :key="cat.id" :value="cat.id">{{ cat.category_Name }}</Option>
</Select>
</FormItem>
</Col>
<Col span="12">
<FormItem label="物料编码" prop="material_code">
<Input v-model="modals.material.form.material_code" placeholder="请输入物料编码" />
</FormItem>
</Col>
</Row>
<Row :gutter="16">
<Col span="12">
<FormItem label="物料名称" prop="material_name">
<Input v-model="modals.material.form.material_name" placeholder="请输入物料名称" />
</FormItem>
</Col>
<Col span="12">
<FormItem label="规格型号" prop="material_spec">
<Input v-model="modals.material.form.material_spec" placeholder="请输入规格型号" />
</FormItem>
</Col>
</Row>
<Row :gutter="16">
<Col span="12">
<FormItem label="单位" prop="unit">
<Input v-model="modals.material.form.unit" placeholder="请输入单位" />
</FormItem>
</Col>
<Col span="12">
<FormItem label="单价" prop="unit_price">
<InputNumber v-model="modals.material.form.unit_price" :min="0" :precision="4" style="width: 100%" />
</FormItem>
</Col>
</Row>
<Row :gutter="16">
<Col span="12">
<FormItem label="存储位置" prop="storage_location">
<Input v-model="modals.material.form.storage_location" placeholder="请输入存储位置" />
</FormItem>
</Col>
<Col span="12">
<FormItem label="物料状态" prop="status">
<Select v-model="modals.material.form.status" style="width: 100%">
<Option :value="0">报废</Option>
<Option :value="1">使用</Option>
<Option :value="2">待修</Option>
</Select>
</FormItem>
</Col>
</Row>
<Row :gutter="16">
<Col span="12">
<FormItem label="最低库存" prop="min_stock">
<InputNumber v-model="modals.material.form.min_stock" :min="0" :precision="4" style="width: 100%" />
</FormItem>
</Col>
<Col span="12">
<FormItem label="最高库存" prop="max_stock">
<InputNumber v-model="modals.material.form.max_stock" :min="0" :precision="4" style="width: 100%" />
</FormItem>
</Col>
</Row>
<Row :gutter="16">
<Col span="12">
<FormItem label="是否可领用" prop="can_borrow">
<Select v-model="modals.material.form.can_borrow" style="width: 100%">
<Option :value="0">消耗品</Option>
<Option :value="1">可领用</Option>
</Select>
</FormItem>
</Col>
<Col span="12">
<FormItem label="排序号" prop="order_no">
<InputNumber v-model="modals.material.form.order_no" :min="0" style="width: 100%" />
</FormItem>
</Col>
</Row>
<FormItem label="物料描述" prop="material_desc">
<Input type="textarea" :rows="3" v-model="modals.material.form.material_desc" placeholder="请输入物料描述" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="modals.material.visible = false">取消</Button>
<Button type="primary" :loading="modals.material.saving" @click="handleSave('material')">保存</Button>
</div>
</Modal>
<!-- 权限配置弹窗 -->
<Modal v-model="permissionModal.visible" title="权限配置" width="600" :mask-closable="false">
<Form :label-width="120">
<FormItem label="分类名称">
<Input :value="permissionModal.categoryName" disabled />
</FormItem>
<FormItem label="权限配置">
<Select v-model="permissionModal.selectedPermissions" multiple clearable filterable placeholder="请选择权限">
<Option v-for="opt in permissionOptions" :key="opt.value" :value="opt.value">{{ opt.label }}</Option>
</Select>
</FormItem>
</Form>
<div slot="footer">
<Button @click="permissionModal.visible = false">取消</Button>
<Button type="primary" :loading="permissionModal.saving" @click="savePermission">保存</Button>
</div>
</Modal>
</div>
</template>
<script>
import {
getUserCategoryList,
saveUserCategory,
deleteUserCategory,
getLeaveTypeList,
saveLeaveType,
deleteLeaveType,
getMaterialCategoryList,
saveMaterialCategory,
deleteMaterialCategory,
getMaterialList,
saveMaterial,
deleteMaterial,
getUserCategoryPermissionList,
saveUserCategoryPermission
} from '@/api/key-dm'
import { getDictList } from '@/api/common'
export default {
name: 'key-dm-index',
data () {
return {
activeTab: 'userCategory',
filters: {
userCategory: { catgory_name: '' },
leaveType: { type_name: '', type_code: '' },
materialCategory: { category_Name: '', category_Code: '' },
material: { material_name: '', material_code: '', category_id: '' }
},
tables: {
userCategory: [],
leaveType: [],
materialCategory: [],
material: []
},
loading: {
userCategory: false,
leaveType: false,
materialCategory: false,
material: false
},
pagers: {
userCategory: { pageNo: 1, pageSize: 10, totalRecord: 0 },
leaveType: { pageNo: 1, pageSize: 10, totalRecord: 0 },
materialCategory: { pageNo: 1, pageSize: 10, totalRecord: 0 },
material: { pageNo: 1, pageSize: 10, totalRecord: 0 }
},
modals: {
userCategory: {
visible: false,
isEdit: false,
saving: false,
form: {
id: '',
catgory_name: '',
remark: '',
order_no: 0
},
rules: {
catgory_name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }]
}
},
leaveType: {
visible: false,
isEdit: false,
saving: false,
form: {
id: '',
type_code: '',
type_name: '',
remark: '',
base_days: 0,
max_days_per_year: 0,
need_approval: 1,
order_no: 0
},
rules: {
type_code: [{ required: true, message: '请输入类型编码', trigger: 'blur' }],
type_name: [{ required: true, message: '请输入类型名称', trigger: 'blur' }]
}
},
materialCategory: {
visible: false,
isEdit: false,
saving: false,
form: {
id: '',
category_Code: '',
category_Name: '',
remark: '',
order_no: 0
},
rules: {
category_Code: [{ required: true, message: '请输入分类编码', trigger: 'blur' }],
category_Name: [{ required: true, message: '请输入分类名称', trigger: 'blur' }]
}
},
material: {
visible: false,
isEdit: false,
saving: false,
form: {
id: '',
category_id: '',
material_code: '',
material_name: '',
material_spec: '',
unit: '',
unit_price: 0,
storage_location: '',
min_stock: 0,
max_stock: 0,
status: 1,
can_borrow: 1,
material_desc: '',
order_no: 0
},
rules: {
category_id: [{ required: true, message: '请选择分类', trigger: 'change' }],
material_name: [{ required: true, message: '请输入物料名称', trigger: 'blur' }]
}
}
},
permissionModal: {
visible: false,
saving: false,
categoryId: '',
categoryName: '',
selectedPermissions: []
},
permissionOptions: [],
materialCategoryOptions: [],
userCategoryColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '分类名称', key: 'catgory_name', align: 'center' },
{ title: '描述', key: 'remark', align: 'center' },
{ title: '排序号', key: 'order_no', align: 'center', width: 100 },
{ title: '操作', slot: 'action', align: 'center', width: 250, fixed: 'right' }
],
leaveTypeColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '类型编码', key: 'type_code', align: 'center' },
{ title: '类型名称', key: 'type_name', align: 'center' },
{ title: '基准天数', key: 'base_days', align: 'center', width: 100 },
{ title: '需要审批', key: 'need_approval', align: 'center', width: 100, render: (h, { row }) => h('span', row.need_approval === 1 ? '需要' : '不需要') },
{ title: '排序号', key: 'order_no', align: 'center', width: 100 },
{ title: '操作', slot: 'action', align: 'center', width: 180, fixed: 'right' }
],
materialCategoryColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '分类编码', key: 'category_Code', align: 'center' },
{ title: '分类名称', key: 'category_Name', align: 'center' },
{ title: '分类描述', key: 'remark', align: 'center' },
{ title: '排序号', key: 'order_no', align: 'center', width: 100 },
{ title: '操作', slot: 'action', align: 'center', width: 180, fixed: 'right' }
],
materialColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '分类', key: 'category_name', align: 'center' },
{ title: '物料编码', key: 'material_code', align: 'center' },
{ title: '物料名称', key: 'material_name', align: 'center' },
{ title: '规格型号', key: 'material_spec', align: 'center' },
{ title: '单位', key: 'unit', align: 'center', width: 80 },
{ title: '单价', key: 'unit_price', align: 'center', width: 100 },
{ title: '物料状态', key: 'status', align: 'center', width: 100, render: (h, { row }) => {
const statusMap = { 0: '报废', 1: '使用', 2: '待修' }
return h('span', statusMap[row.status] || '-')
}},
{ title: '操作', slot: 'action', align: 'center', width: 180, fixed: 'right' }
]
}
},
created () {
this.loadPermissionDict()
this.loadMaterialCategoryOptions()
this.fetchList(this.activeTab)
},
methods: {
handleTabChange (name) {
this.activeTab = name
this.fetchList(name)
},
fetchList (tab) {
const apiMap = {
userCategory: getUserCategoryList,
leaveType: getLeaveTypeList,
materialCategory: getMaterialCategoryList,
material: getMaterialList
}
const api = apiMap[tab]
if (!api) return
this.loading[tab] = true
const payload = Object.assign({}, this.pagers[tab], { params: this.filters[tab] })
api(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const data = ret.data.data || {}
this.tables[tab] = data.results || []
this.pagers[tab].totalRecord = data.totalRecord || 0
} else {
this.$Notice.error({ title: '查询失败', desc: ret.data.errmsg || '请稍后重试' })
}
}).finally(() => { this.loading[tab] = false })
},
handleSearch (tab) {
this.pagers[tab].pageNo = 1
this.fetchList(tab)
},
handleReset (tab) {
const resetMap = {
userCategory: () => ({ catgory_name: '' }),
leaveType: () => ({ type_name: '', type_code: '' }),
materialCategory: () => ({ category_Name: '', category_Code: '' }),
material: () => ({ material_name: '', material_code: '', category_id: '' })
}
this.filters[tab] = resetMap[tab]()
this.pagers[tab].pageNo = 1
this.fetchList(tab)
},
pageChange (tab, pageNo) {
this.pagers[tab].pageNo = pageNo
this.fetchList(tab)
},
sizeChange (tab, size) {
this.pagers[tab].pageSize = size
this.pagers[tab].pageNo = 1
this.fetchList(tab)
},
openAddModal (tab) {
const modal = this.modals[tab]
modal.visible = true
modal.isEdit = false
modal.form = this.getDefaultForm(tab)
this.$nextTick(() => {
if (this.$refs[tab + 'Form']) {
this.$refs[tab + 'Form'].resetFields()
}
})
},
openEditModal (tab, row) {
const modal = this.modals[tab]
modal.visible = true
modal.isEdit = true
modal.form = Object.assign({}, this.getDefaultForm(tab), row)
this.$nextTick(() => {
if (this.$refs[tab + 'Form']) {
this.$refs[tab + 'Form'].resetFields()
}
})
},
getDefaultForm (tab) {
const defaults = {
userCategory: {
id: '',
catgory_name: '',
remark: '',
order_no: 0
},
leaveType: {
id: '',
type_code: '',
type_name: '',
remark: '',
base_days: 0,
max_days_per_year: 0,
need_approval: 1,
order_no: 0
},
materialCategory: {
id: '',
category_Code: '',
category_Name: '',
remark: '',
order_no: 0
},
material: {
id: '',
category_id: '',
material_code: '',
material_name: '',
material_spec: '',
unit: '',
unit_price: 0,
storage_location: '',
min_stock: 0,
max_stock: 0,
status: 1,
can_borrow: 1,
material_desc: '',
order_no: 0
}
}
return JSON.parse(JSON.stringify(defaults[tab]))
},
handleSave (tab) {
const modal = this.modals[tab]
const formRef = this.$refs[tab + 'Form']
if (!formRef) return
formRef.validate(valid => {
if (!valid) return
modal.saving = true
const saveApiMap = {
userCategory: saveUserCategory,
leaveType: saveLeaveType,
materialCategory: saveMaterialCategory,
material: saveMaterial
}
const api = saveApiMap[tab]
api(modal.form).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('保存成功')
modal.visible = false
this.fetchList(tab)
} else {
this.$Notice.error({ title: '保存失败', desc: ret.data.errmsg || '请稍后重试' })
}
}).finally(() => { modal.saving = false })
})
},
handleDelete (tab, row) {
const deleteApiMap = {
userCategory: deleteUserCategory,
leaveType: deleteLeaveType,
materialCategory: deleteMaterialCategory,
material: deleteMaterial
}
const api = deleteApiMap[tab]
api({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('删除成功')
this.fetchList(tab)
} else {
this.$Notice.error({ title: '删除失败', desc: ret.data.errmsg || '请稍后重试' })
}
})
},
openPermissionModal (row) {
this.permissionModal.visible = true
this.permissionModal.categoryId = row.id
this.permissionModal.categoryName = row.catgory_name
this.permissionModal.selectedPermissions = []
// 加载已有权限
getUserCategoryPermissionList({ category_id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const list = ret.data.data || ret.data.results || []
this.permissionModal.selectedPermissions = list.map(item => item.permission_mark).filter(Boolean)
}
})
},
savePermission () {
if (!this.permissionModal.categoryId) {
this.$Message.warning('分类ID不存在')
return
}
this.permissionModal.saving = true
const permissions = Array.isArray(this.permissionModal.selectedPermissions) ? this.permissionModal.selectedPermissions : []
const permissionList = permissions.map(mark => {
const opt = this.permissionOptions.find(p => p.value === mark)
return {
category_id: this.permissionModal.categoryId,
permission_mark: mark,
permission_name: opt ? opt.label : mark
}
})
saveUserCategoryPermission({
category_id: this.permissionModal.categoryId,
permissions: permissionList
}).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('保存成功')
this.permissionModal.visible = false
this.fetchList('userCategory')
} else {
this.$Notice.error({ title: '保存失败', desc: ret.data.errmsg || '请稍后重试' })
}
}).finally(() => { this.permissionModal.saving = false })
},
loadPermissionDict () {
// 从字典获取权限选项,字典类型为 permission_mark
getDictList({ type: 'permission_mark' }).then(res => {
if (res.data && res.data.errcode === 0) {
const results = res.data.data && res.data.data.results ? res.data.data.results : []
this.permissionOptions = results.map(it => ({
label: it.label || it.name || '',
value: it.value || it.dictValue || ''
})).filter(it => it.label && it.value)
}
})
},
loadMaterialCategoryOptions () {
getMaterialCategoryList({ pageNo: 1, pageSize: 1000, params: {} }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const data = ret.data.data || {}
this.materialCategoryOptions = data.results || []
}
})
}
}
}
</script>
<style scoped>
.key-dm-wrapper {
padding: 10px;
}
.search-div {
border: 1px solid #dce1e7;
padding: 15px;
margin-bottom: 15px;
background-color: #f8fbff;
}
.mr5 {
margin-right: 5px;
}
.mr10 {
margin-right: 10px;
}
.text-right {
text-align: right;
}
.page_style {
margin-top: 12px;
text-align: right;
}
</style>
<template>
<div class="key-dm-inbound-wrapper">
<Tabs v-model="activeTab" @on-click="handleTabChange">
<TabPane label="办公用品入库" name="inbound">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="8">
<Input v-model="filters.inbound.inbound_no" placeholder="单号/批次号" style="width:60%" />
<Button type="primary" class="mr10" @click="handleSearch('inbound')">搜索</Button>
<Button @click="handleReset('inbound')">重置</Button>
</Col>
<Col span="16" class="text-right">
<Button type="success" @click="openInboundModal">新增入库</Button>
</Col>
</Row>
</div>
<Table :data="tables.inbound" :loading="loading.inbound" border>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="inbound_no" title="入库单号" align="center" />
<TableColumn prop="batch_no" title="批次号" align="center" />
<TableColumn prop="inbound_date" title="入库日期" align="center" />
<TableColumn prop="inbound_type" title="类型" align="center" />
<TableColumn prop="inbound_status" title="状态" align="center" />
<TableColumn title="操作" width="360" align="center">
<template slot-scope="{ row }">
<Button size="small" @click="openDetail(row)">详细</Button>
<Button size="small" type="primary" @click="openEdit(row)" v-if="canEdit(row)">修改</Button>
<Poptip confirm title="确认删除?" transfer @on-ok="deleteInbound(row)">
<Button size="small" type="error">删除</Button>
</Poptip>
<Poptip confirm title="确认执行入库?" transfer @on-ok="doInbound(row)" v-if="row.inbound_status===0">
<Button size="small" type="success">入库</Button>
</Poptip>
</template>
</TableColumn>
</Table>
<Page class="page_style" :total="pagers.inbound.totalRecord" :current="pagers.inbound.pageNo" :page-size="pagers.inbound.pageSize"
show-total show-sizer @on-change="pageChange('inbound', $event)" @on-page-size-change="sizeChange('inbound', $event)" />
</TabPane>
<TabPane label="办公用品归还" name="return">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="10">
<Input v-model="filters.return.key" placeholder="申请单号/申请人" style="width:60%" />
<Button type="primary" class="mr10" @click="handleSearch('return')">搜索</Button>
<Button @click="handleReset('return')">重置</Button>
</Col>
</Row>
</div>
<Table :data="tables.return" :loading="loading.return" border>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="application_no" title="申请单号" align="center" />
<TableColumn prop="applicant_name" title="申请人" align="center" />
<TableColumn prop="department_name" title="部门" align="center" />
<TableColumn prop="issue_status" title="发放状态" align="center" />
<TableColumn title="操作" width="140" align="center">
<template slot-scope="{ row }">
<Button size="small" type="primary" @click="openReturnModal(row)">归还</Button>
</template>
</TableColumn>
</Table>
<Page class="page_style" :total="pagers.return.totalRecord" :current="pagers.return.pageNo" :page-size="pagers.return.pageSize"
show-total show-sizer @on-change="pageChange('return', $event)" @on-page-size-change="sizeChange('return', $event)" />
</TabPane>
<TabPane label="库存查询" name="inventory">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="10">
<Input v-model="filters.inventory.material_name" placeholder="物料名称/编码" style="width:60%" />
<Button type="primary" class="mr10" @click="handleSearch('inventory')">搜索</Button>
<Button @click="handleReset('inventory')">重置</Button>
</Col>
</Row>
</div>
<Table :data="tables.inventory" :loading="loading.inventory" border>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="material_code" title="物料编码" align="center" />
<TableColumn prop="material_name" title="物料名称" align="center" />
<TableColumn prop="total_quantity" title="总量" align="center" />
<TableColumn prop="available_quantity" title="可用" align="center" />
<TableColumn prop="borrowed_quantity" title="借出" align="center" />
</Table>
</TabPane>
</Tabs>
<!-- 入库弹窗 -->
<Modal v-model="inboundModal.visible" :title="inboundModal.isEdit ? '修改入库' : '新增入库'" width="800">
<Form :model="inboundModal.form" :label-width="120" ref="inboundForm">
<FormItem label="入库单号">
<Input v-model="inboundModal.form.inbound_no" />
</FormItem>
<FormItem label="批次号">
<Input v-model="inboundModal.form.batch_no" />
</FormItem>
<FormItem label="入库日期">
<DatePicker v-model="inboundModal.form.inbound_date" type="date" />
</FormItem>
<FormItem label="存放位置">
<Input v-model="inboundModal.form.storage_location" />
</FormItem>
<FormItem label="备注">
<Input type="textarea" v-model="inboundModal.form.remark" :rows="2" />
</FormItem>
<h4>明细(从物料库选择)</h4>
<Table :data="inboundModal.details" size="small" border>
<TableColumn prop="material_code" title="物料编码" />
<TableColumn prop="material_name" title="物料名称" />
<TableColumn prop="inbound_quantity" title="入库数量" />
<TableColumn prop="unit_price" title="单价" />
<TableColumn prop="total_amount" title="总金额" />
</Table>
</Form>
<div slot="footer">
<Button @click="inboundModal.visible=false">取消</Button>
<Button type="primary" :loading="inboundModal.saving" @click="saveInbound">保存</Button>
</div>
</Modal>
<!-- 归还弹窗 -->
<Modal v-model="returnModal.visible" title="归还物品" width="700">
<Form :model="returnModal.record" :label-width="120">
<FormItem label="申请单号"><span>{{ returnModal.record.application_no }}</span></FormItem>
<FormItem label="申请人"><span>{{ returnModal.record.applicant_name }}</span></FormItem>
<FormItem label="归还明细">
<Table :data="returnModal.details" size="small" border>
<TableColumn prop="material_name" title="物料名称" />
<TableColumn prop="apply_quantity" title="申请数量" />
<TableColumn prop="returned_quantity" title="归还数量" />
</Table>
</FormItem>
</Form>
<div slot="footer">
<Button @click="returnModal.visible=false">取消</Button>
<Button type="primary" :loading="returnModal.saving" @click="confirmReturn">归还</Button>
</div>
</Modal>
<!-- 详情弹窗 -->
<Modal v-model="detailModal.visible" title="入库/归还详情" width="720">
<Spin fix v-if="detailModal.loading"></Spin>
<div v-else>
<Row :gutter="16"><Col span="12"><p><strong>单号:</strong>{{ detailModal.data.inbound_no || detailModal.data.application_no }}</p></Col></Row>
<Row class="mt8"><Col span="24"><h4>明细</h4></Col></Row>
<Table :data="detailModal.details" size="small" border>
<TableColumn prop="material_code" title="物料编码" />
<TableColumn prop="material_name" title="物料名称" />
<TableColumn prop="inbound_quantity" title="数量" />
<TableColumn prop="unit_price" title="单价" />
</Table>
<Row class="mt8"><Col span="24"><h4>操作日志</h4></Col></Row>
<Table :data="detailModal.logs" size="small" border>
<TableColumn prop="op_type" title="操作类型" />
<TableColumn prop="quantity" title="数量" />
<TableColumn prop="create_time" title="时间" />
</Table>
</div>
<div slot="footer"><Button type="primary" @click="detailModal.visible=false">关闭</Button></div>
</Modal>
</div>
</template>
<script>
import {
getInboundList,
saveInbound,
deleteInbound,
doInbound,
getPendingReturnList,
processReturn,
getInventoryList,
getInboundById
} from '@/api/key-dm-inbound'
export default {
name: 'key-dm-inbound-index',
data () {
return {
activeTab: 'inbound',
filters: { inbound: { inbound_no: '' }, return: { key: '' }, inventory: { material_name: '' } },
tables: { inbound: [], return: [], inventory: [] },
pagers: { inbound: { pageNo:1,pageSize:10,totalRecord:0 }, return: { pageNo:1,pageSize:10,totalRecord:0 }, inventory: { pageNo:1,pageSize:10,totalRecord:0 } },
loading: { inbound:false, return:false, inventory:false },
inboundModal: { visible:false,isEdit:false,saving:false,form:{},details:[] },
returnModal: { visible:false,record:{},details:[],saving:false },
detailModal: { visible:false,loading:false,data:{},details:[],logs:[] }
}
},
created () { this.fetchList('inbound') },
methods: {
handleTabChange (name) {
this.activeTab = name
if (name === 'return') this.fetchList('return')
if (name === 'inventory') this.fetchList('inventory')
},
fetchList (tab) {
const apiMap = { inbound: getInboundList, return: getPendingReturnList, inventory: getInventoryList }
const api = apiMap[tab]; if (!api) return
this.loading[tab] = true
const payload = Object.assign({}, this.pagers[tab] || {}, { params: this.filters[tab] || {} })
api(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const data = ret.data.data || {}
this.tables[tab] = data.results || []
if (data.totalRecord !== undefined) (this.pagers[tab]||{}).totalRecord = data.totalRecord
} else this.$Notice.error({ title:'查询失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.loading[tab]=false })
},
handleSearch (tab) { (this.pagers[tab]||{}).pageNo=1; this.fetchList(tab) },
handleReset (tab) { this.filters[tab] = {}; (this.pagers[tab]||{}).pageNo=1; this.fetchList(tab) },
pageChange (tab,pageNo) { (this.pagers[tab]||{}).pageNo=pageNo; this.fetchList(tab) },
sizeChange (tab,size) { (this.pagers[tab]||{}).pageSize=size; (this.pagers[tab]||{}).pageNo=1; this.fetchList(tab) },
openInboundModal () {
this.inboundModal.isEdit = false
this.inboundModal.form = { inbound_no:'', batch_no:'', inbound_date:'', inbound_type:1, storage_location:'', remark:'' }
this.inboundModal.details = []
this.inboundModal.visible = true
},
openEdit (row) {
this.inboundModal.isEdit = true
getInboundById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.inboundModal.form = ret.data.data || {}
this.inboundModal.details = ret.data.data.details || []
}
})
this.inboundModal.visible = true
},
saveInbound () {
this.inboundModal.saving = true
saveInbound(Object.assign({}, this.inboundModal.form, { details: this.inboundModal.details })).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('保存成功'); this.inboundModal.visible=false; this.fetchList('inbound') } else this.$Notice.error({ title:'保存失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.inboundModal.saving=false })
},
canEdit (row) { return row.inbound_status === 0 },
deleteInbound (row) { deleteInbound({ id: row.id }).then(ret => { if (ret.data && ret.data.errcode === 0) { this.$Message.success('删除成功'); this.fetchList('inbound') } else this.$Notice.error({ title:'删除失败', desc: ret.data && ret.data.errmsg }) }) },
doInbound (row) { doInbound({ id: row.id }).then(ret=>{ if (ret.data && ret.data.errcode === 0) { this.$Message.success('入库成功'); this.fetchList('inbound'); this.fetchList('inventory') } else this.$Notice.error({ title:'入库失败', desc: ret.data && ret.data.errmsg }) }) },
openReturnModal (row) {
this.returnModal.record = Object.assign({}, row)
this.returnModal.details = []
getInboundById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) this.returnModal.details = ret.data.data.details || []
})
this.returnModal.visible = true
},
confirmReturn () {
this.returnModal.saving = true
processReturn({ id: this.returnModal.record.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('归还已入库并更新库存'); this.returnModal.visible=false; this.fetchList('return'); this.fetchList('inventory') } else this.$Notice.error({ title:'归还失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.returnModal.saving=false })
},
openDetail (row) {
this.detailModal.visible = true
this.detailModal.loading = true
getInboundById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.detailModal.data = ret.data.data || {}
this.detailModal.details = ret.data.data.details || []
this.detailModal.logs = ret.data.data.logs || []
} else this.$Notice.error({ title:'查询失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.detailModal.loading=false })
}
}
}
</script>
<style scoped>
.key-dm-inbound-wrapper { padding: 10px; }
.search-div { border: 1px solid #dce1e7; padding: 12px; margin-bottom: 12px; background-color: #f8fbff; }
.mr10 { margin-right: 10px; }
.text-right { text-align: right; }
.page_style { margin-top: 12px; text-align: right; }
.mt8 { margin-top: 8px; }
</style>
<template>
<div class="key-dm-inventory-wrapper">
<Tabs v-model="activeTab" @on-click="handleTabChange">
<TabPane label="办公用品申领" name="apply">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="10">
<Input v-model="filters.apply.applicant_name" placeholder="姓名/工号" style="width:60%" />
<Button type="primary" class="mr10" @click="handleSearch('apply')">搜索</Button>
<Button @click="handleReset('apply')">重置</Button>
</Col>
<Col span="14" class="text-right">
<Button type="success" @click="openApplyModal">申请</Button>
</Col>
</Row>
</div>
<Table :data="tables.apply" :loading="loading.apply" border>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="application_no" title="申请单号" align="center" />
<TableColumn prop="applicant_name" title="申请人" align="center" />
<TableColumn prop="department_name" title="部门" align="center" />
<TableColumn prop="approval_status" title="状态" align="center" />
<TableColumn prop="submit_time" title="提交时间" align="center" />
<TableColumn title="操作" width="320" align="center">
<template slot-scope="{ row }">
<Button size="small" @click="openDetail(row)">详细</Button>
<Button size="small" type="primary" @click="openEdit(row)" v-if="canEdit(row)">修改</Button>
<Button size="small" type="success" @click="submit(row)" v-if="canSubmit(row)">提交</Button>
<Poptip confirm title="确认撤回?" transfer @on-ok="revoke(row)" v-if="canRevoke(row)">
<Button size="small" type="warning">撤回</Button>
</Poptip>
</template>
</TableColumn>
</Table>
<Page class="page_style" :total="pagers.apply.totalRecord" :current="pagers.apply.pageNo" :page-size="pagers.apply.pageSize"
show-total show-sizer @on-change="pageChange('apply', $event)" @on-page-size-change="sizeChange('apply', $event)" />
</TabPane>
<TabPane label="办公用品待审核" name="pending">
<Table :data="tables.pending" :loading="loading.pending" border>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="application_no" title="申请单号" align="center" />
<TableColumn prop="applicant_name" title="申请人" align="center" />
<TableColumn prop="department_name" title="部门" align="center" />
<TableColumn prop="submit_time" title="提交时间" align="center" />
<TableColumn title="操作" width="140" align="center">
<template slot-scope="{ row }">
<Button size="small" type="primary" @click="openApproveModal(row)">处理</Button>
</template>
</TableColumn>
</Table>
<Page class="page_style" :total="pagers.pending.totalRecord" :current="pagers.pending.pageNo" :page-size="pagers.pending.pageSize"
show-total show-sizer @on-change="pageChange('pending', $event)" @on-page-size-change="sizeChange('pending', $event)" />
</TabPane>
<TabPane label="办公用品审核历史查询" name="history">
<Table :data="tables.history" :loading="loading.history" border>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="application_no" title="申请单号" align="center" />
<TableColumn prop="applicant_name" title="申请人" align="center" />
<TableColumn prop="approval_status" title="状态" align="center" />
<TableColumn prop="approval_time" title="审批时间" align="center" />
<TableColumn title="操作" width="100" align="center">
<template slot-scope="{ row }">
<Button size="small" @click="openDetail(row)">详细</Button>
</template>
</TableColumn>
</Table>
<Page class="page_style" :total="pagers.history.totalRecord" :current="pagers.history.pageNo" :page-size="pagers.history.pageSize"
show-total show-sizer @on-change="pageChange('history', $event)" @on-page-size-change="sizeChange('history', $event)" />
</TabPane>
</Tabs>
<!-- 申请弹窗 -->
<Modal v-model="applyModal.visible" :title="applyModal.isEdit ? '修改领用申请' : '新增领用申请'" width="800">
<Form :model="applyModal.form" :label-width="120" ref="applyForm">
<FormItem label="申请人">
<Input v-model="applyModal.form.applicant_name" disabled />
</FormItem>
<FormItem label="部门">
<Input v-model="applyModal.form.department_name" disabled />
</FormItem>
<FormItem label="领用用途">
<Input v-model="applyModal.form.borrow_purpose" />
</FormItem>
<FormItem label="预计归还日期">
<DatePicker v-model="applyModal.form.expected_return_date" type="date" />
</FormItem>
<h4>申请明细</h4>
<Table :data="applyModal.details" size="small" border>
<TableColumn prop="material_code" title="物料编码" />
<TableColumn prop="material_name" title="物料名称" />
<TableColumn prop="apply_quantity" title="申请数量" />
<TableColumn prop="unit" title="单位" />
<TableColumn prop="issue_remark" title="备注" />
</Table>
</Form>
<div slot="footer">
<Button @click="applyModal.visible=false">取消</Button>
<Button type="primary" :loading="applyModal.saving" @click="saveApplication">保存</Button>
</div>
</Modal>
<!-- 审核弹窗 -->
<Modal v-model="approveModal.visible" title="申请处理" width="700">
<Form :model="approveModal.record" :label-width="120">
<FormItem label="申请单号"><span>{{ approveModal.record.application_no }}</span></FormItem>
<FormItem label="申请人"><span>{{ approveModal.record.applicant_name }}</span></FormItem>
<FormItem label="申请明细">
<Table :data="approveModal.details" size="small" border>
<TableColumn prop="material_name" title="物料名称" />
<TableColumn prop="apply_quantity" title="申请数量" />
<TableColumn prop="unit" title="单位" />
<TableColumn prop="available_quantity" title="可用库存" />
</Table>
</FormItem>
<FormItem label="审批意见">
<Input type="textarea" v-model="approveModal.opinion" placeholder="驳回时必填" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="approveModal.visible=false">取消</Button>
<Button type="success" :loading="approveModal.submitting" @click="confirmApprove">通过</Button>
<Button type="error" :loading="approveModal.submitting" @click="confirmReject">驳回</Button>
</div>
</Modal>
<!-- 详情弹窗 -->
<Modal v-model="detailModal.visible" title="申请详情" width="720">
<Spin fix v-if="detailModal.loading"></Spin>
<div v-else>
<Row :gutter="16"><Col span="12"><p><strong>申请人:</strong>{{ detailModal.data.applicant_name }}</p></Col><Col span="12"><p><strong>部门:</strong>{{ detailModal.data.department_name }}</p></Col></Row>
<Row class="mt8"><Col span="24"><p><strong>领用用途:</strong>{{ detailModal.data.borrow_purpose }}</p></Col></Row>
<Row class="mt8"><Col span="24"><h4>明细</h4></Col></Row>
<Table :data="detailModal.details" size="small" border>
<TableColumn prop="material_code" title="物料编码" />
<TableColumn prop="material_name" title="物料名称" />
<TableColumn prop="apply_quantity" title="申请数量" />
<TableColumn prop="unit" title="单位" />
<TableColumn prop="returned_quantity" title="已归还" />
</Table>
<Row class="mt8"><Col span="24"><h4>审批记录</h4></Col></Row>
<Table :data="detailModal.logs" size="small" border>
<TableColumn prop="op_type" title="操作类型" />
<TableColumn prop="quantity" title="数量" />
<TableColumn prop="create_time" title="时间" />
<TableColumn prop="create_by" title="操作人" />
</Table>
</div>
<div slot="footer"><Button type="primary" @click="detailModal.visible=false">关闭</Button></div>
</Modal>
</div>
</template>
<script>
import {
getBorrowList,
saveBorrowApplication,
submitBorrow,
revokeBorrow,
getPendingBorrowList,
approveBorrow,
rejectBorrow,
getBorrowById
} from '@/api/key-dm-inventory'
export default {
name: 'key-dm-inventory-index',
data () {
return {
activeTab: 'apply',
filters: { apply: { applicant_name: '' } },
tables: { apply: [], pending: [], history: [] },
pagers: { apply: { pageNo:1,pageSize:10,totalRecord:0 }, pending: { pageNo:1,pageSize:10,totalRecord:0 }, history: { pageNo:1,pageSize:10,totalRecord:0 } },
loading: { apply:false,pending:false,history:false },
applyModal: { visible:false,isEdit:false,saving:false,form:{},details:[] },
approveModal: { visible:false,record:{},details:[],opinion:'',submitting:false },
detailModal: { visible:false,loading:false,data:{},details:[],logs:[] }
}
},
created () { this.fetchList('apply') },
methods: {
handleTabChange (name) {
this.activeTab = name
if (name === 'pending') this.fetchList('pending')
if (name === 'history') this.fetchList('history')
},
fetchList (tab) {
const apiMap = { apply: getBorrowList, pending: getPendingBorrowList, history: getBorrowList }
const api = apiMap[tab]; if (!api) return
this.loading[tab] = true
const payload = Object.assign({}, this.pagers[tab] || {}, { params: this.filters[tab] || {} })
api(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const data = ret.data.data || {}
this.tables[tab] = data.results || []
if (data.totalRecord !== undefined) (this.pagers[tab]||{}).totalRecord = data.totalRecord
} else this.$Notice.error({ title:'查询失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.loading[tab]=false })
},
handleSearch (tab) { (this.pagers[tab]||{}).pageNo=1; this.fetchList(tab) },
handleReset (tab) { this.filters[tab] = {}; (this.pagers[tab]||{}).pageNo=1; this.fetchList(tab) },
pageChange (tab,pageNo) { (this.pagers[tab]||{}).pageNo=pageNo; this.fetchList(tab) },
sizeChange (tab,size) { (this.pagers[tab]||{}).pageSize=size; (this.pagers[tab]||{}).pageNo=1; this.fetchList(tab) },
openApplyModal () {
this.applyModal.isEdit = false
this.applyModal.form = { applicant_id:'', applicant_name:'', department_id:'', department_name:'', borrow_purpose:'', expected_return_date:'', approval_status:0 }
this.applyModal.details = []
this.applyModal.visible = true
},
openEdit (row) {
this.applyModal.isEdit = true
this.applyModal.form = Object.assign({}, row)
// load details from backend when open (simplified)
getBorrowById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.applyModal.details = ret.data.data.details || []
}
})
this.applyModal.visible = true
},
saveApplication () {
this.applyModal.saving = true
const payload = Object.assign({}, this.applyModal.form, { details: this.applyModal.details })
saveBorrowApplication(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('保存成功'); this.applyModal.visible=false; this.fetchList('apply') } else this.$Notice.error({ title:'保存失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.applyModal.saving=false })
},
canEdit (row) { return row.approval_status === 0 || row.approval_status === -1 },
canSubmit (row) { return row.approval_status === 0 || row.approval_status === -1 },
canRevoke (row) { return row.approval_status === 1 },
submit (row) {
this.$Modal.confirm({ title:'确认提交', content:'提交后进入审批流程,是否继续?', onOk: () => {
submitBorrow({ id: row.id }).then(ret => { if (ret.data && ret.data.errcode === 0) { this.$Message.success('提交成功'); this.fetchList('apply') } else this.$Notice.error({ title:'提交失败', desc: ret.data && ret.data.errmsg }) })
}})
},
revoke (row) { revokeBorrow({ id: row.id }).then(ret=>{ if (ret.data && ret.data.errcode === 0){ this.$Message.success('撤回成功'); this.fetchList('apply') } else this.$Notice.error({ title:'撤回失败', desc: ret.data && ret.data.errmsg }) }) },
openApproveModal (row) {
this.approveModal.record = Object.assign({}, row)
this.approveModal.opinion = ''
// load details and current inventory for display
getBorrowById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.approveModal.details = ret.data.data.details || []
}
})
this.approveModal.visible = true
},
confirmApprove () {
this.approveModal.submitting = true
approveBorrow({ id: this.approveModal.record.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('已通过,已生成出库并更新库存'); this.approveModal.visible=false; this.fetchList('pending'); this.fetchList('apply') } else this.$Notice.error({ title:'操作失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.approveModal.submitting=false })
},
confirmReject () {
if (!this.approveModal.opinion || !this.approveModal.opinion.trim()) { this.$Message.warning('请输入驳回原因'); return }
this.approveModal.submitting = true
rejectBorrow({ id: this.approveModal.record.id, comment: this.approveModal.opinion }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('已驳回'); this.approveModal.visible=false; this.fetchList('pending') } else this.$Notice.error({ title:'驳回失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.approveModal.submitting=false })
},
openDetail (row) {
this.detailModal.visible = true
this.detailModal.loading = true
getBorrowById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.detailModal.data = ret.data.data || {}
this.detailModal.details = ret.data.data.details || []
this.detailModal.logs = ret.data.data.logs || []
} else this.$Notice.error({ title:'查询失败', desc: ret.data && ret.data.errmsg })
}).finally(()=>{ this.detailModal.loading=false })
}
}
}
</script>
<style scoped>
.key-dm-inventory-wrapper { padding: 10px; }
.search-div { border: 1px solid #dce1e7; padding: 12px; margin-bottom: 12px; background-color: #f8fbff; }
.mr10 { margin-right: 10px; }
.text-right { text-align: right; }
.page_style { margin-top: 12px; text-align: right; }
.mt8 { margin-top: 8px; }
</style>
<template>
<div class="key-dm-stats-wrapper">
<Tabs v-model="activeTab" @on-click="handleTabChange">
<TabPane label="办公用品使用统计" name="usage">
<div class="search-div">
<Row type="flex" :gutter="16" align="middle">
<Col :span="8">
<span>时间段:</span>
<DatePicker v-model="usageRange" type="daterange" style="width:70%" />
</Col>
<Col :span="8">
<span>物料:</span>
<Input v-model="usageMaterial" placeholder="物料名称或编码" style="width:70%" />
</Col>
<Col :span="8" class="text-right">
<Button type="primary" class="mr10" @click="loadUsage">统计</Button>
<Button @click="resetUsage">重置</Button>
</Col>
</Row>
</div>
<Table :data="tables.usage" :loading="loading.usage" border>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="department_name" title="部门" align="center" />
<TableColumn prop="material_name" title="物料" align="center" />
<TableColumn prop="total_quantity" title="领用数量" align="center" />
<TableColumn title="操作" width="100" align="center">
<template slot-scope="{ row }">
<Button size="small" @click="openUsageDetail(row)">详细</Button>
</template>
</TableColumn>
</Table>
</TabPane>
<TabPane label="工作量统计" name="workload">
<div class="search-div">
<Row type="flex" :gutter="16" align="middle">
<Col :span="10">
<span>时间段:</span>
<DatePicker v-model="workRange" type="daterange" style="width:70%" />
</Col>
<Col :span="6" class="text-right">
<Button type="primary" class="mr10" @click="loadWorkload">统计</Button>
<Button @click="resetWorkload">重置</Button>
</Col>
</Row>
</div>
<Table :data="tables.workload" :loading="loading.workload" border>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="user_name" title="人员" align="center" />
<TableColumn prop="leave_applications" title="请假申请次数" align="center" />
<TableColumn prop="leave_approvals" title="请假审批次数" align="center" />
<TableColumn prop="borrow_applications" title="用品申请次数" align="center" />
<TableColumn prop="inbound_count" title="入库次数" align="center" />
<TableColumn prop="leave_duration" title="请假时长(天)" align="center" />
<TableColumn title="操作" width="160" align="center">
<template slot-scope="{ row }">
<Button size="small" @click="openWorkloadDetail(row,'leave')">请假明细</Button>
<Button size="small" class="ml5" @click="openWorkloadDetail(row,'inventory')">用品明细</Button>
</template>
</TableColumn>
</Table>
</TabPane>
</Tabs>
<!-- 使用明细弹窗 -->
<Modal v-model="detailModal.visible" title="明细" width="800">
<Table :data="detailModal.rows" size="small" border>
<TableColumn v-for="col in detailModal.columns" :key="col.key" :prop="col.key" :title="col.title" />
</Table>
<div slot="footer"><Button type="primary" @click="detailModal.visible=false">关闭</Button></div>
</Modal>
</div>
</template>
<script>
import { getUsageStats, getUsageDetails, getWorkloadStats, getWorkloadDetails } from '@/api/key-dm-stats'
export default {
name: 'key-dm-stats-index',
data () {
return {
activeTab: 'usage',
usageRange: [],
usageMaterial: '',
workRange: [],
tables: { usage: [], workload: [] },
loading: { usage: false, workload: false },
detailModal: { visible: false, rows: [], columns: [] }
}
},
methods: {
handleTabChange (name) {
this.activeTab = name
},
loadUsage () {
this.loading.usage = true
const params = { start: this.usageRange[0], end: this.usageRange[1], material: this.usageMaterial }
getUsageStats({ params }).then(ret => {
if (ret.data && ret.data.errcode === 0) this.tables.usage = ret.data.data || []
else this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.loading.usage = false })
},
resetUsage () { this.usageRange = []; this.usageMaterial = ''; this.tables.usage = [] },
openUsageDetail (row) {
this.detailModal.columns = [{ key: 'user_name', title: '人员' }, { key: 'quantity', title: '数量' }, { key: 'material_name', title: '物料' }, { key: 'department_name', title: '部门' }]
this.detailModal.visible = true
getUsageDetails({ params: { start: this.usageRange[0], end: this.usageRange[1], material: this.usageMaterial, department: row.department_id } }).then(ret => {
if (ret.data && ret.data.errcode === 0) this.detailModal.rows = ret.data.data || []
else this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
})
},
loadWorkload () {
this.loading.workload = true
const params = { start: this.workRange[0], end: this.workRange[1] }
getWorkloadStats({ params }).then(ret => {
if (ret.data && ret.data.errcode === 0) this.tables.workload = ret.data.data || []
else this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.loading.workload = false })
},
resetWorkload () { this.workRange = []; this.tables.workload = [] },
openWorkloadDetail (row, type) {
if (type === 'leave') {
this.detailModal.columns = [{ key: 'user_name', title: '人员' }, { key: 'start_time', title: '开始' }, { key: 'end_time', title: '结束' }, { key: 'duration', title: '时长' }]
getWorkloadDetails({ params: { user_id: row.user_id, start: this.workRange[0], end: this.workRange[1], type: 'leave' } }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.detailModal.rows = ret.data.data || []; this.detailModal.visible = true } else this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
})
} else {
this.detailModal.columns = [{ key: 'application_no', title: '申请单号' }, { key: 'material_name', title: '物料' }, { key: 'apply_quantity', title: '数量' }]
getWorkloadDetails({ params: { user_id: row.user_id, start: this.workRange[0], end: this.workRange[1], type: 'inventory' } }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.detailModal.rows = ret.data.data || []; this.detailModal.visible = true } else this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
})
}
}
}
}
</script>
<style scoped>
.key-dm-stats-wrapper { padding: 10px; }
.search-div { border: 1px solid #dce1e7; padding: 12px; margin-bottom: 12px; background-color: #f8fbff; }
.mr10 { margin-right: 10px; }
.text-right { text-align: right; }
.page_style { margin-top: 12px; text-align: right; }
.mt8 { margin-top: 8px; }
</style>
<template>
<div class="key-dm-leave-wrapper">
<Tabs v-model="activeTab" @on-click="handleTabChange">
<TabPane label="请假申请" name="apply">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="8">
<span>姓名:</span>
<Input v-model="filters.apply.name" placeholder="请输入姓名" style="width: 60%" />
</Col>
<Col span="8" class="text-right">
<Button type="primary" class="mr10" @click="handleSearch('apply')">搜索</Button>
<Button class="mr10" @click="handleReset('apply')">重置</Button>
<Button type="success" @click="openApplyModal">申请</Button>
</Col>
</Row>
</div>
<Table border :loading="loading.apply" :columns="applyColumns" :data="tables.apply">
<template slot="action" slot-scope="{ row }">
<Button size="small" @click="openDetail(row)">详细</Button>
<Button size="small" type="primary" class="mr5" @click="openEdit(row)" v-if="canEdit(row)">修改</Button>
<Button size="small" type="success" class="mr5" @click="submit(row)" v-if="canSubmit(row)">提交</Button>
<Poptip confirm title="确认撤回?" transfer @on-ok="revoke(row)" v-if="canRevoke(row)">
<Button size="small" type="warning">撤回</Button>
</Poptip>
</template>
</Table>
<Page class="page_style" :total="pagers.apply.totalRecord" :current="pagers.apply.pageNo" :page-size="pagers.apply.pageSize"
show-total show-sizer @on-change="pageChange('apply', $event)" @on-page-size-change="sizeChange('apply', $event)" />
</TabPane>
<TabPane label="请假待审核" name="pending">
<Table border :loading="loading.pending" :columns="pendingColumns" :data="tables.pending">
<template slot="action" slot-scope="{ row }">
<Button size="small" type="primary" @click="openApproveModal(row)">处理</Button>
</template>
</Table>
<Page class="page_style" :total="pagers.pending.totalRecord" :current="pagers.pending.pageNo" :page-size="pagers.pending.pageSize"
show-total show-sizer @on-change="pageChange('pending', $event)" @on-page-size-change="sizeChange('pending', $event)" />
</TabPane>
<TabPane label="审核历史查询" name="history">
<Table border :loading="loading.history" :columns="historyColumns" :data="tables.history">
<template slot="action" slot-scope="{ row }">
<Button size="small" @click="openHistoryDetail(row)">详细</Button>
</template>
</Table>
<Page class="page_style" :total="pagers.history.totalRecord" :current="pagers.history.pageNo" :page-size="pagers.history.pageSize"
show-total show-sizer @on-change="pageChange('history', $event)" @on-page-size-change="sizeChange('history', $event)" />
</TabPane>
<TabPane label="请假申请查询" name="query">
<Table border :loading="loading.query" :columns="queryColumns" :data="tables.query">
<template slot="action" slot-scope="{ row }">
<Button size="small" @click="openDetail(row)">详细</Button>
</template>
</Table>
<Page class="page_style" :total="pagers.query.totalRecord" :current="pagers.query.pageNo" :page-size="pagers.query.pageSize"
show-total show-sizer @on-change="pageChange('query', $event)" @on-page-size-change="sizeChange('query', $event)" />
</TabPane>
<TabPane label="请假统计" name="stats">
<div class="search-div">
<Row type="flex" :gutter="16" align="middle">
<Col span="8">
<span>时间段:</span>
<DatePicker v-model="statsRange" type="daterange" placeholder="选择时间段" style="width:70%" />
</Col>
<Col span="8">
<span>部门:</span>
<Input v-model="statsDept" placeholder="请输入部门名称或ID" style="width:65%" />
</Col>
<Col span="8" class="text-right">
<Button type="primary" class="mr10" @click="loadStats">统计</Button>
<Button @click="resetStats">重置</Button>
</Col>
</Row>
</div>
<Table border :loading="loading.stats" :columns="statsColumns" :data="tables.stats" />
</TabPane>
</Tabs>
<!-- 申请弹窗 -->
<Modal v-model="applyModal.visible" :title="applyModal.isEdit ? '修改请假' : '新增请假'" width="700">
<Form :model="applyModal.form" :label-width="120" ref="applyForm">
<FormItem label="请假类型" prop="leave_type_id">
<Select v-model="applyModal.form.leave_type_id" style="width: 60%">
<Option v-for="opt in leaveTypes" :key="opt.id" :value="opt.id">{{ opt.type_name }}</Option>
</Select>
</FormItem>
<FormItem label="开始时间" prop="start_time">
<DatePicker v-model="applyModal.form.start_time" type="datetime" placeholder="请选择开始时间" />
</FormItem>
<FormItem label="结束时间" prop="end_time">
<DatePicker v-model="applyModal.form.end_time" type="datetime" placeholder="请选择结束时间" />
</FormItem>
<FormItem label="请假事由" prop="reason">
<Input type="textarea" v-model="applyModal.form.reason" :rows="3" />
</FormItem>
<FormItem label="紧急联系人">
<Input v-model="applyModal.form.emergency_contact" />
</FormItem>
<FormItem label="紧急电话">
<Input v-model="applyModal.form.emergency_phone" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="applyModal.visible = false">取消</Button>
<Button type="primary" :loading="applyModal.saving" @click="saveApply">保存</Button>
</div>
</Modal>
<!-- 审核弹窗 -->
<Modal v-model="approveModal.visible" title="请假审批" width="700">
<Form :model="approveModal.record" :label-width="120">
<FormItem label="申请人">
<span>{{ approveModal.record.user_name }}</span>
</FormItem>
<FormItem label="请假类型">
<span>{{ approveModal.record.leave_type_name }}</span>
</FormItem>
<FormItem label="起止时间">
<span>{{ formatDatetime(approveModal.record.start_time) }} - {{ formatDatetime(approveModal.record.end_time) }}</span>
</FormItem>
<FormItem label="请假事由">
<span>{{ approveModal.record.reason }}</span>
</FormItem>
<FormItem label="审批意见" prop="comment">
<Input type="textarea" v-model="approveModal.comment" :rows="3" placeholder="请输入审批意见(驳回时必填)" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="approveModal.visible = false">取消</Button>
<Button type="success" :loading="approveModal.submitting" @click="confirmApprove">通过</Button>
<Button type="error" :loading="approveModal.submitting" @click="confirmReject">驳回</Button>
</div>
</Modal>
<!-- 详情弹窗(申请 + 审核记录) -->
<Modal v-model="detailModal.visible" title="请假详情" width="720">
<Spin fix v-if="detailModal.loading"></Spin>
<div v-else>
<Row :gutter="16">
<Col span="12"><p><strong>申请人:</strong>{{ detailModal.data.user_name || '-' }}</p></Col>
<Col span="12"><p><strong>部门:</strong>{{ detailModal.data.department_name || '-' }}</p></Col>
</Row>
<Row class="mt8"><Col span="24"><p><strong>请假类型:</strong>{{ detailModal.data.leave_type_name || '-' }}</p></Col></Row>
<Row class="mt8"><Col span="24"><p><strong>起止时间:</strong>{{ formatDatetime(detailModal.data.start_time) }} - {{ formatDatetime(detailModal.data.end_time) }}</p></Col></Row>
<Row class="mt8"><Col span="24"><p><strong>请假事由:</strong>{{ detailModal.data.reason || '-' }}</p></Col></Row>
<Row class="mt8"><Col span="24"><h4>审批记录</h4></Col></Row>
<Table :data="detailModal.approvals" size="small" border>
<TableColumn title="节点" key="node_index" />
<TableColumn title="审批人" key="approver_name" />
<TableColumn title="结果" key="approver_result" :render="renderApproveResult" />
<TableColumn title="时间" key="approver_time" />
<TableColumn title="意见" key="comment" />
</Table>
</div>
<div slot="footer"><Button type="primary" @click="detailModal.visible=false">关闭</Button></div>
</Modal>
</div>
</template>
<script>
import {
getLeaveList,
saveLeaveApplication,
submitLeaveApplication,
revokeLeaveApplication,
getPendingList,
approveLeave,
rejectLeave,
getApprovalHistory,
getLeaveStats,
getLeaveById
} from '@/api/key-dm-leave'
import { getDictList } from '@/api/common'
export default {
name: 'key-dm-leave-index',
data () {
return {
activeTab: 'apply',
filters: { apply: { name: '' } },
tables: { apply: [], pending: [], history: [], query: [], stats: [] },
pagers: { apply: { pageNo: 1, pageSize: 10, totalRecord: 0 }, pending: { pageNo: 1, pageSize: 10, totalRecord: 0 }, history: { pageNo: 1, pageSize: 10, totalRecord: 0 }, query: { pageNo: 1, pageSize: 10, totalRecord: 0 } },
loading: { apply: false, pending: false, history: false, query: false, stats: false },
leaveTypes: [],
applyColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '申请人', key: 'user_name', align: 'center' },
{ title: '部门', key: 'department_name', align: 'center' },
{ title: '请假类型', key: 'leave_type_name', align: 'center' },
{ title: '起止时间', key: 'start_time', align: 'center', render: (h, { row }) => h('span', `${row.start_time || '-'} ~ ${row.end_time || '-'}`) },
{ title: '时长', key: 'duration', align: 'center' },
{ title: '状态', key: 'status', align: 'center' },
{ title: '操作', slot: 'action', align: 'center', width: 320 }
],
pendingColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '申请人', key: 'user_name', align: 'center' },
{ title: '请假类型', key: 'leave_type_name', align: 'center' },
{ title: '起止时间', key: 'start_time', align: 'center' },
{ title: '提交时间', key: 'submit_time', align: 'center' },
{ title: '操作', slot: 'action', align: 'center', width: 120 }
],
historyColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '申请人', key: 'user_name', align: 'center' },
{ title: '请假类型', key: 'leave_type_name', align: 'center' },
{ title: '起止时间', key: 'start_time', align: 'center' },
{ title: '审批完成时间', key: 'approval_complete_time', align: 'center' },
{ title: '操作', slot: 'action', align: 'center', width: 100 }
],
queryColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '申请人', key: 'user_name', align: 'center' },
{ title: '状态', key: 'status', align: 'center' },
{ title: '提交时间', key: 'submit_time', align: 'center' },
{ title: '操作', slot: 'action', align: 'center', width: 100 }
],
statsColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '人员', key: 'user_name', align: 'center' },
{ title: '部门', key: 'department_name', align: 'center' },
{ title: '已通过天数', key: 'passed_days', align: 'center' }
],
applyModal: { visible: false, isEdit: false, saving: false, form: {} },
approveModal: { visible: false, record: {}, comment: '', submitting: false },
detailModal: { visible: false, loading: false, data: {}, approvals: [] },
statsRange: [],
statsDept: ''
}
},
created () {
this.loadLeaveTypes()
this.fetchList('apply')
},
methods: {
handleTabChange (name) {
this.activeTab = name
if (name === 'pending') this.fetchList('pending')
if (name === 'history') this.fetchList('history')
if (name === 'query') this.fetchList('query')
},
fetchList (tab) {
const apiMap = { apply: getLeaveList, pending: getPendingList, history: getApprovalHistory, query: getLeaveList }
const api = apiMap[tab]
if (!api) return
this.loading[tab] = true
const payload = Object.assign({}, this.pagers[tab] || {}, { params: this.filters[tab] || {} })
api(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const data = ret.data.data || {}
if (tab === 'history') this.tables.history = data.results || []
else if (tab === 'pending') this.tables.pending = data.results || []
else if (tab === 'query') this.tables.query = data.results || []
else this.tables.apply = data.results || []
if (data.totalRecord !== undefined) (this.pagers[tab] || {}).totalRecord = data.totalRecord
} else {
this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
}
}).finally(() => { this.loading[tab] = false })
},
handleSearch (tab) { (this.pagers[tab] || {}).pageNo = 1; this.fetchList(tab) },
handleReset (tab) { this.filters[tab] = {}; (this.pagers[tab] || {}).pageNo = 1; this.fetchList(tab) },
pageChange (tab, pageNo) { (this.pagers[tab] || {}).pageNo = pageNo; this.fetchList(tab) },
sizeChange (tab, size) { (this.pagers[tab] || {}).pageSize = size; (this.pagers[tab] || {}).pageNo = 1; this.fetchList(tab) },
loadLeaveTypes () {
getDictList({ type: 'leave_type' }).then(res => {
const list = res.data && (res.data.data || res.data.results) || []
this.leaveTypes = Array.isArray(list) ? list.map(it => ({ id: it.value || it.dictValue, type_name: it.label || it.name })) : []
})
},
openApplyModal () {
this.applyModal.isEdit = false
this.applyModal.form = { user_id: '', user_name: '', leave_type_id: '', start_time: '', end_time: '', duration_unit: 2, duration: 0, reason: '', emergency_contact: '', emergency_phone: '' }
this.applyModal.visible = true
},
openEdit (row) {
this.applyModal.isEdit = true
this.applyModal.form = Object.assign({}, row)
this.applyModal.visible = true
},
saveApply () {
this.applyModal.saving = true
saveLeaveApplication(this.applyModal.form).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('保存成功')
this.applyModal.visible = false
this.fetchList('apply')
} else this.$Notice.error({ title: '保存失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.applyModal.saving = false })
},
canEdit (row) { return row.status === 0 || row.status === -1 },
canSubmit (row) { return row.status === 0 || row.status === -1 },
canRevoke (row) { return row.status === 1 },
submit (row) {
this.$Modal.confirm({ title: '确认提交', content: '确认提交此请假申请进入审批流程?', onOk: () => {
submitLeaveApplication({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('提交成功'); this.fetchList('apply') } else this.$Notice.error({ title: '提交失败', desc: ret.data && ret.data.errmsg })
})
}})
},
revoke (row) {
revokeLeaveApplication({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('撤回成功'); this.fetchList('apply') } else this.$Notice.error({ title: '撤回失败', desc: ret.data && ret.data.errmsg })
})
},
openApproveModal (row) {
this.approveModal.record = Object.assign({}, row)
this.approveModal.comment = ''
this.approveModal.visible = true
},
confirmApprove () {
this.approveModal.submitting = true
approveLeave({ id: this.approveModal.record.id, comment: this.approveModal.comment }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('审批通过'); this.approveModal.visible = false; this.fetchList('pending') } else this.$Notice.error({ title: '审批失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.approveModal.submitting = false })
},
confirmReject () {
if (!this.approveModal.comment || !this.approveModal.comment.trim()) { this.$Message.warning('请输入驳回原因'); return }
this.approveModal.submitting = true
rejectLeave({ id: this.approveModal.record.id, comment: this.approveModal.comment }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('已驳回'); this.approveModal.visible = false; this.fetchList('pending') } else this.$Notice.error({ title: '驳回失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.approveModal.submitting = false })
},
openDetail (row) {
this.detailModal.visible = true
this.detailModal.loading = true
getLeaveById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.detailModal.data = ret.data.data || {}
this.detailModal.approvals = ret.data.data.approvals || []
} else this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.detailModal.loading = false })
},
openHistoryDetail (row) { this.openDetail(row) },
renderApproveResult (h, { row }) { return row.approver_result === 1 ? '通过' : (row.approver_result === -1 ? '驳回' : '未处理') },
formatDatetime (val) { if (!val) return '-'; return String(val).replace('T',' ') },
loadStats () {
this.loading.stats = true
const params = { start: this.statsRange && this.statsRange[0], end: this.statsRange && this.statsRange[1], department: this.statsDept }
getLeaveStats({ params }).then(ret => {
if (ret.data && ret.data.errcode === 0) this.tables.stats = ret.data.data || [] else this.$Notice.error({ title: '统计失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.loading.stats = false })
},
resetStats () { this.statsRange = []; this.statsDept = ''; this.tables.stats = [] }
}
}
</script>
<style scoped>
.key-dm-leave-wrapper { padding: 10px; }
.search-div { border: 1px solid #dce1e7; padding: 12px; margin-bottom: 12px; background-color: #f8fbff; }
.mr10 { margin-right: 10px; }
.text-right { text-align: right; }
.page_style { margin-top: 12px; text-align: right; }
.mt8 { margin-top: 8px; }
</style>
<template>
<div class="key-dm-user-wrapper">
<Card>
<div class="search-div">
<Row type="flex" :gutter="16" align="middle">
<Col :span="8">
<span>机构编码:</span>
<Input v-model="officeCode" placeholder="请输入机构编码或留空为当前机构" style="width: 60%" />
<Button type="primary" class="mr10" @click="handleSync">同步</Button>
<Button @click="fetchList">刷新</Button>
</Col>
<Col :span="8" class="text-right">
<span>姓名:</span>
<Input v-model="filters.name" placeholder="请输入姓名" style="width: 40%" />
<Button type="primary" class="mr10" @click="handleSearch">搜索</Button>
<Button @click="handleReset">重置</Button>
</Col>
</Row>
</div>
<Table :data="rows" :loading="loading" border>
<template slot="index" slot-scope="{ index }">
<span>{{ index + 1 }}</span>
</template>
<TableColumn type="index" title="序号" width="60" align="center" />
<TableColumn prop="name" title="姓名" align="center" />
<TableColumn prop="gh" title="工号" align="center" />
<TableColumn prop="office_name" title="归属部门" align="center" />
<TableColumn prop="email" title="邮箱" align="center" />
<TableColumn prop="phone" title="电话" align="center" />
<TableColumn prop="mobile" title="手机" align="center" />
<TableColumn prop="is_leave" title="离职" width="80" align="center" :render="renderIsLeave" />
<TableColumn prop="is_ext" title="同步来源" width="120" align="center" :render="renderIsExt" />
<TableColumn title="操作" width="200" align="center">
<template slot-scope="{ row }">
<Button size="small" type="primary" @click="openEdit(row)">修改</Button>
</template>
</TableColumn>
</Table>
<Page class="page_style" :total="pager.totalRecord" :current="pager.pageNo" :page-size="pager.pageSize"
show-total show-sizer @on-change="pageChange" @on-page-size-change="sizeChange" />
</Card>
<Modal v-model="editModal.visible" title="编辑日常人员" width="700" :mask-closable="false">
<Form :model="editModal.form" :label-width="120" ref="editForm">
<FormItem label="姓名">
<Input v-model="editModal.form.name" disabled />
</FormItem>
<FormItem label="工号">
<Input v-model="editModal.form.gh" disabled />
</FormItem>
<FormItem label="邮箱">
<Input v-model="editModal.form.email" />
</FormItem>
<FormItem label="电话">
<Input v-model="editModal.form.phone" />
</FormItem>
<FormItem label="手机">
<Input v-model="editModal.form.mobile" />
</FormItem>
<FormItem label="出生日期">
<DatePicker v-model="editModal.form.birthday" type="date" placeholder="请选择日期" />
</FormItem>
<FormItem label="参加工作时间">
<DatePicker v-model="editModal.form.in_work_time" type="date" placeholder="请选择日期" />
</FormItem>
<FormItem label="归属部门">
<Input v-model="editModal.form.office_id" placeholder="请输入机构ID(可通过列表选择扩展)" />
</FormItem>
<FormItem label="直属领导ID">
<Input v-model="editModal.form.leader" placeholder="只能选择本级或父机构人员ID" />
</FormItem>
<FormItem label="是否离职">
<Select v-model="editModal.form.is_leave" style="width: 160px">
<Option :value="0">未离职</Option>
<Option :value="1">离职</Option>
</Select>
</FormItem>
</Form>
<div slot="footer">
<Button @click="editModal.visible = false">取消</Button>
<Button type="primary" :loading="editModal.saving" @click="saveEdit">保存</Button>
</div>
</Modal>
</div>
</template>
<script>
import { getDmUserList, syncDmUsersByOffice, saveDmUser } from '@/api/key-dm-user'
export default {
name: 'key-dm-user-index',
data () {
return {
officeCode: '',
filters: { name: '' },
rows: [],
loading: false,
pager: { pageNo: 1, pageSize: 10, totalRecord: 0 },
editModal: {
visible: false,
saving: false,
form: {}
}
}
},
created () {
this.fetchList()
},
methods: {
renderIsLeave (h, { row }) {
return row.is_leave === 1 ? '是' : '否'
},
renderIsExt (h, { row }) {
return row.is_ext === 1 ? '系统同步' : '手工录入'
},
fetchList () {
this.loading = true
const payload = { pageNo: this.pager.pageNo, pageSize: this.pager.pageSize, params: Object.assign({}, this.filters) }
getDmUserList(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const data = ret.data.data || {}
this.rows = data.results || []
this.pager.totalRecord = data.totalRecord || 0
} else {
this.$Notice.error({ title: '查询失败', desc: ret.data && ret.data.errmsg })
}
}).finally(() => { this.loading = false })
},
handleSearch () {
this.pager.pageNo = 1
this.fetchList()
},
handleReset () {
this.filters = { name: '' }
this.pager.pageNo = 1
this.fetchList()
},
pageChange (pageNo) {
this.pager.pageNo = pageNo
this.fetchList()
},
sizeChange (size) {
this.pager.pageSize = size
this.pager.pageNo = 1
this.fetchList()
},
handleSync () {
const params = { office_code: this.officeCode }
this.$Modal.confirm({
title: '确认同步',
content: '将同步本机构及下属机构的用户,已存在的记录会被更新(不覆盖 null 值),是否继续?',
onOk: () => {
this.loading = true
syncDmUsersByOffice(params).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('同步完成')
this.fetchList()
} else {
this.$Notice.error({ title: '同步失败', desc: ret.data && ret.data.errmsg })
}
}).finally(() => { this.loading = false })
}
})
},
openEdit (row) {
this.editModal.form = Object.assign({}, row)
this.editModal.visible = true
},
saveEdit () {
this.$refs.editForm.validate(valid => {
// no strict validation here, just save
this.editModal.saving = true
saveDmUser(this.editModal.form).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('保存成功')
this.editModal.visible = false
this.fetchList()
} else {
this.$Notice.error({ title: '保存失败', desc: ret.data && ret.data.errmsg })
}
}).finally(() => { this.editModal.saving = false })
})
}
}
}
</script>
<style scoped>
.key-dm-user-wrapper { padding: 10px; }
.search-div { border: 1px solid #dce1e7; padding: 12px; margin-bottom: 12px; background-color: #f8fbff; }
.mr10 { margin-right: 10px; }
.page_style { margin-top: 12px; text-align: right; }
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论