Merge branch 'dev_NEW_pro' into dev_山西晋中_平遥盛达
| | |
| | | "logo": "logo/SDZZLogo.png", |
| | | "favicon": "favicon/SDZZfavicon.ico" |
| | | }, |
| | | "QLMC": { |
| | | "env": { |
| | | "VITE_APP_TITLE": "祁连牧场信息管理系统", |
| | | "VITE_BASE_API": "http://114.132.189.42:9066", |
| | | "VITE_JAVA_API": "http://114.132.189.42:9060" |
| | | }, |
| | | "logo": "logo/QLMCLogo.png", |
| | | "favicon": "favicon/QLMCico.ico" |
| | | }, |
| | | "logo": "/src/assets/logo/logo.png", |
| | | "favicon": "/public/favicon.ico" |
| | | } |
| | |
| | | { label: "退货单号", prop: "returnNo", minWidth: "150" }, |
| | | { label: "供应商", prop: "supplierName", minWidth: "180" }, |
| | | { label: "关联入库单号", prop: "inboundBatches", minWidth: "150" }, |
| | | { |
| | | label: "发货类型", |
| | | prop: "shippingType", |
| | | minWidth: "110", |
| | | formatData: (val) => ({ 1: "货车", 2: "快递" }[String(val)] || "--"), |
| | | }, |
| | | { |
| | | label: "发货车牌号", |
| | | prop: "truckPlateNo", |
| | | minWidth: "140", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "1" ? row?.truckPlateNo || "--" : "--"), |
| | | }, |
| | | { |
| | | label: "快递公司", |
| | | prop: "expressCompany", |
| | | minWidth: "140", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "2" ? row?.expressCompany || "--" : "--"), |
| | | }, |
| | | { |
| | | label: "快递单号", |
| | | prop: "expressNo", |
| | | minWidth: "150", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "2" ? row?.expressNo || "--" : "--"), |
| | | }, |
| | | { label: "退货日期", prop: "preparedAt", minWidth: "170" }, |
| | | { |
| | | label: "退款总额", |
| | |
| | | { label: "退货单号", prop: "returnNo", minWidth: "150" }, |
| | | { label: "客户名称", prop: "customerName", minWidth: "180" }, |
| | | { label: "关联发货单号", prop: "shippingNo", minWidth: "150" }, |
| | | { |
| | | label: "发货类型", |
| | | prop: "shippingType", |
| | | minWidth: "110", |
| | | formatData: (val) => ({ 1: "货车", 2: "快递" }[String(val)] || "--"), |
| | | }, |
| | | { |
| | | label: "发货车牌号", |
| | | prop: "truckPlateNo", |
| | | minWidth: "140", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "1" ? row?.truckPlateNo || "--" : "--"), |
| | | }, |
| | | { |
| | | label: "快递公司", |
| | | prop: "expressCompany", |
| | | minWidth: "140", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "2" ? row?.expressCompany || "--" : "--"), |
| | | }, |
| | | { |
| | | label: "快递单号", |
| | | prop: "expressNo", |
| | | minWidth: "150", |
| | | formatData: (_val, row) => (String(row?.shippingType) === "2" ? row?.expressNo || "--" : "--"), |
| | | }, |
| | | { label: "退货日期", prop: "makeTime", minWidth: "170" }, |
| | | { |
| | | label: "退款总额", |
| | |
| | | pathPrefixes: ["/inventorymanagement"], |
| | | }, |
| | | production: { |
| | | titles: ["生产管理", "主生产计划", "生产订单", "生产报工"], |
| | | pathPrefixes: ["/productionmanagement", "/productionplan"], |
| | | titles: ["生产管控"], |
| | | pathPrefixes: ["/productionManagement", "/productionPlan"], |
| | | }, |
| | | quality: { |
| | | titles: ["质量管理"], |
| | |
| | | realtime: visibleModules.value.production, |
| | | quick: quickEntries.value.length > 0, |
| | | plan: visibleModules.value.production, |
| | | receipt: visibleModules.value.sales || visibleModules.value.finance, |
| | | receipt: visibleModules.value.finance, |
| | | })); |
| | | |
| | | const hasLeftPanels = computed( |
| | |
| | | } |
| | | |
| | | .stats-grid { |
| | | display: grid; |
| | | grid-template-columns: repeat(4, minmax(0, 1fr)); |
| | | display: flex; |
| | | gap: 14px; |
| | | } |
| | | |
| | | .stats-grid .stat-card { |
| | | flex: 1; |
| | | } |
| | | |
| | | .stat-card { |
| | |
| | | .main-grid { |
| | | grid-template-columns: minmax(0, 1fr) 340px; |
| | | } |
| | | |
| | | .stats-grid { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | } |
| | | |
| | | @media (max-width: 1200px) { |
| | |
| | | |
| | | .meta-extra { |
| | | justify-content: flex-start; |
| | | } |
| | | |
| | | .stats-grid { |
| | | grid-template-columns: repeat(2, minmax(0, 1fr)); |
| | | } |
| | | |
| | | .right-column { |
| | |
| | | filterable |
| | | > |
| | | <el-option |
| | | v-for="o in getOptions(field)" |
| | | v-for="o in getOptions(field, { moduleKey: props.moduleKey })" |
| | | :key="String(o.value)" |
| | | :label="o.label" |
| | | :value="o.value" |
| | |
| | | fields: { type: Array, default: () => [] }, |
| | | formPayload: { type: Object, default: () => ({}) }, |
| | | readonly: { type: Boolean, default: false }, |
| | | moduleKey: { type: String, default: "" }, |
| | | }); |
| | | |
| | | const { loading: optionSourceLoading, ensureForFields, getOptions, getDisplayLabel } = |
| | | useSelectOptionSources(); |
| | | |
| | | async function loadOptionCaches() { |
| | | await ensureForFields(props.fields); |
| | | await ensureForFields(props.fields, { moduleKey: props.moduleKey }); |
| | | } |
| | | |
| | | onMounted(() => { |
| | |
| | | function displayValue(field) { |
| | | const val = props.formPayload?.[field.key]; |
| | | if (field.type === "select" && field.optionSource && field.optionSource !== "static") { |
| | | return getDisplayLabel(field, val); |
| | | return getDisplayLabel(field, val, { moduleKey: props.moduleKey }); |
| | | } |
| | | return formatFieldDisplayValue(field, val); |
| | | } |
| | |
| | | else if (row.businessType === 5) { |
| | | const purchaseContractNumber = row?.purchaseContractNumber; |
| | | if (purchaseContractNumber) { |
| | | const res = await getPurchaseByCode({ purchaseContractNumber }); |
| | | const res = await getPurchaseByCode({ |
| | | purchaseContractNumber, |
| | | approvalInstanceId: row?.id, |
| | | }); |
| | | detailData.value = res || {}; |
| | | } |
| | | } |
| | |
| | | else if (row.businessType === 5) { |
| | | const purchaseContractNumber = row?.purchaseContractNumber; |
| | | if (purchaseContractNumber) { |
| | | const res = await getPurchaseByCode({ purchaseContractNumber }); |
| | | const res = await getPurchaseByCode({ |
| | | purchaseContractNumber, |
| | | approvalInstanceId: row?.id, |
| | | }); |
| | | detailData.value = res || {}; |
| | | } |
| | | } |
| | |
| | | v-model:attachments="form.storageBlobDTOs" |
| | | :template-attachments="form.templateAttachments" |
| | | :user-options="userOptions" |
| | | :module-key="moduleKey" |
| | | :show-template-name="!isEdit" |
| | | :allow-change-template="false" |
| | | :flow-attachments-only="flowAttachmentsOnly" |
| | |
| | | fields: { type: Array, default: () => [] }, |
| | | activeTemplate: { type: Object, default: null }, |
| | | userOptions: { type: Array, default: () => [] }, |
| | | moduleKey: { type: String, default: "" }, |
| | | isEdit: { type: Boolean, default: false }, |
| | | saving: { type: Boolean, default: false }, |
| | | formRef: { type: Object, default: null }, |
| | |
| | | 用法: |
| | | <ApprovalTemplateBindDialog |
| | | v-model:visible="visible" |
| | | module-key="regular" |
| | | :module-key="APPROVAL_MODULE_KEYS.REGULAR" |
| | | @confirm="onTemplateBound" |
| | | /> |
| | | --> |
| | |
| | | v-if="!hideFormFields && !flowAttachmentsOnly && !flowOnly" |
| | | :fields="fields" |
| | | :form-payload="formPayload" |
| | | :module-key="moduleKey" |
| | | /> |
| | | |
| | | <el-form-item label="审批流程" required> |
| | |
| | | uploadLimit: { type: Number, default: 10 }, |
| | | /** 为 true 时可编辑模板预置的审批人(仅审批模板管理页使用) */ |
| | | flowEditable: { type: Boolean, default: false }, |
| | | moduleKey: { type: String, default: "" }, |
| | | }); |
| | | |
| | | const emit = defineEmits(["update:flowNodes", "update:attachments", "change-template"]); |
| | |
| | | import { deptTreeSelect, userListNoPageByTenantId } from "@/api/system/user.js"; |
| | | import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js"; |
| | | |
| | | /** 下拉选项来源(写入 formConfig,提交页按来源拉取数据) */ |
| | | export const SELECT_OPTION_SOURCE = { |
| | | STATIC: "static", |
| | | USER: "user", |
| | | DEPT: "dept", |
| | | EMPLOYEE: "employee", |
| | | }; |
| | | |
| | | export const SELECT_OPTION_SOURCE_OPTIONS = [ |
| | | { value: SELECT_OPTION_SOURCE.STATIC, label: "手动配置", desc: "在模板中自定义选项文本与值" }, |
| | | { value: SELECT_OPTION_SOURCE.USER, label: "人员列表", desc: "从系统用户中选择,值为用户 ID" }, |
| | | { value: SELECT_OPTION_SOURCE.DEPT, label: "部门列表", desc: "从组织架构中选择,值为部门 ID" }, |
| | | { value: SELECT_OPTION_SOURCE.EMPLOYEE, label: "员工列表", desc: "从在职员工中选择,值为员工 ID" }, |
| | | ]; |
| | | |
| | | const EMPLOYEE_APPLICANT_MODULE_KEYS = new Set(["regular"]); |
| | | |
| | | export function selectOptionSourceLabel(source) { |
| | | return SELECT_OPTION_SOURCE_OPTIONS.find((x) => x.value === source)?.label || "—"; |
| | | } |
| | | |
| | | export function isDynamicOptionSource(source) { |
| | | return source === SELECT_OPTION_SOURCE.USER || source === SELECT_OPTION_SOURCE.DEPT; |
| | | return ( |
| | | source === SELECT_OPTION_SOURCE.USER || |
| | | source === SELECT_OPTION_SOURCE.DEPT || |
| | | source === SELECT_OPTION_SOURCE.EMPLOYEE |
| | | ); |
| | | } |
| | | |
| | | function unwrapArray(payload) { |
| | | if (Array.isArray(payload)) return payload; |
| | | if (Array.isArray(payload?.records)) return payload.records; |
| | | if (payload?.data && Array.isArray(payload.data)) return payload.data; |
| | | if (Array.isArray(payload?.data?.records)) return payload.data.records; |
| | | if (payload?.rows && Array.isArray(payload.rows)) return payload.rows; |
| | | if (Array.isArray(payload?.data?.rows)) return payload.data.rows; |
| | | return []; |
| | | } |
| | | |
| | |
| | | const value = u.userId ?? u.id; |
| | | return { |
| | | label: u.nickName || u.userName || `用户${value}`, |
| | | value, |
| | | }; |
| | | } |
| | | |
| | | /** 在职员工 → 下拉 option */ |
| | | export function mapEmployeeToSelectOption(u) { |
| | | const value = u.id ?? u.staffOnJobId ?? u.staffId; |
| | | return { |
| | | label: u.staffName || u.name || u.userName || `员工${value}`, |
| | | value, |
| | | }; |
| | | } |
| | |
| | | } |
| | | |
| | | /** 按字段配置解析下拉 options(需传入已加载的缓存) */ |
| | | export function resolveFieldSelectOptions(field, caches = {}) { |
| | | function isApplicantField(field) { |
| | | const key = String(field?.key || "").toLowerCase(); |
| | | const label = String(field?.label || ""); |
| | | return ( |
| | | key === "applicant" || |
| | | key === "applicantname" || |
| | | label.includes("申请人") |
| | | ); |
| | | } |
| | | |
| | | export function resolveFieldSelectOptions(field, caches = {}, context = {}) { |
| | | const source = field?.optionSource || SELECT_OPTION_SOURCE.STATIC; |
| | | if ( |
| | | source === SELECT_OPTION_SOURCE.USER && |
| | | EMPLOYEE_APPLICANT_MODULE_KEYS.has(context.moduleKey) && |
| | | isApplicantField(field) |
| | | ) { |
| | | return (caches.employees || []).map(mapEmployeeToSelectOption); |
| | | } |
| | | if (source === SELECT_OPTION_SOURCE.USER) { |
| | | return (caches.users || []).map(mapUserToSelectOption); |
| | | } |
| | | if (source === SELECT_OPTION_SOURCE.DEPT) { |
| | | return caches.deptOptions || []; |
| | | } |
| | | if (source === SELECT_OPTION_SOURCE.EMPLOYEE) { |
| | | return (caches.employees || []).map(mapEmployeeToSelectOption); |
| | | } |
| | | return (field?.options || []).filter((o) => o.value !== "" && o.value != null); |
| | | } |
| | | |
| | | /** 根据已解析的 options 反查展示文本 */ |
| | | export function resolveSelectDisplayLabel(field, val, caches = {}) { |
| | | export function resolveSelectDisplayLabel(field, val, caches = {}, context = {}) { |
| | | if (val == null || val === "") return "—"; |
| | | const options = resolveFieldSelectOptions(field, caches); |
| | | const options = resolveFieldSelectOptions(field, caches, context); |
| | | const hit = options.find((o) => String(o.value) === String(val)); |
| | | return hit?.label || String(val); |
| | | } |
| | |
| | | export async function fetchSelectOptionCaches(sources = []) { |
| | | const needUser = sources.includes(SELECT_OPTION_SOURCE.USER); |
| | | const needDept = sources.includes(SELECT_OPTION_SOURCE.DEPT); |
| | | const caches = { users: [], deptOptions: [] }; |
| | | const needEmployee = sources.includes(SELECT_OPTION_SOURCE.EMPLOYEE); |
| | | const caches = { users: [], deptOptions: [], employees: [] }; |
| | | |
| | | if (!needUser && !needDept) return caches; |
| | | if (!needUser && !needDept && !needEmployee) return caches; |
| | | |
| | | const tasks = []; |
| | | if (needUser) { |
| | |
| | | }) |
| | | ); |
| | | } |
| | | if (needEmployee) { |
| | | tasks.push( |
| | | staffOnJobListPage({ current: -1, size: -1, staffState: 1 }) |
| | | .then((res) => { |
| | | caches.employees = unwrapArray(res); |
| | | }) |
| | | .catch(() => { |
| | | caches.employees = []; |
| | | }) |
| | | ); |
| | | } |
| | | |
| | | await Promise.all(tasks); |
| | | return caches; |
| | | } |
| | | |
| | | /** 从字段列表收集需要预加载的动态来源 */ |
| | | export function collectOptionSourcesFromFields(fields) { |
| | | export function collectOptionSourcesFromFields(fields, context = {}) { |
| | | const set = new Set(); |
| | | (fields || []).forEach((f) => { |
| | | if (f?.type === "select" && isDynamicOptionSource(f.optionSource)) { |
| | | if ( |
| | | EMPLOYEE_APPLICANT_MODULE_KEYS.has(context.moduleKey) && |
| | | f.optionSource === SELECT_OPTION_SOURCE.USER && |
| | | isApplicantField(f) |
| | | ) { |
| | | set.add(SELECT_OPTION_SOURCE.EMPLOYEE); |
| | | } else { |
| | | set.add(f.optionSource); |
| | | } |
| | | } |
| | | }); |
| | | return [...set]; |
| | | } |
| | |
| | | deptOptions: [], |
| | | }); |
| | | |
| | | async function ensureForFields(fields) { |
| | | const sources = collectOptionSourcesFromFields(fields); |
| | | async function ensureForFields(fields, context = {}) { |
| | | const sources = collectOptionSourcesFromFields(fields, context); |
| | | if (!sources.length) return; |
| | | loading.value = true; |
| | | try { |
| | | const next = await fetchSelectOptionCaches(sources); |
| | | caches.users = next.users; |
| | | caches.deptOptions = next.deptOptions; |
| | | caches.employees = next.employees || []; |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } |
| | | |
| | | function getOptions(field) { |
| | | return resolveFieldSelectOptions(field, caches); |
| | | function getOptions(field, context = {}) { |
| | | return resolveFieldSelectOptions(field, caches, context); |
| | | } |
| | | |
| | | function getDisplayLabel(field, val) { |
| | | return resolveSelectDisplayLabel(field, val, caches); |
| | | function getDisplayLabel(field, val, context = {}) { |
| | | return resolveSelectDisplayLabel(field, val, caches, context); |
| | | } |
| | | |
| | | return { |
| | |
| | | :fields="submitFormFields" |
| | | :active-template="activeTemplate" |
| | | :user-options="flowUserOptions" |
| | | :module-key="APPROVAL_MODULE_KEYS.REGULAR" |
| | | :is-edit="isSubmitEdit" |
| | | :saving="submitSaving" |
| | | :form-ref="submitFormRef" |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? '新增离职' : '编辑离职'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | @close="closeDia"> |
| | | <!-- 员工信息展示区域 --> |
| | | <div class="info-section"> |
| | | <div class="info-title">员工信息</div> |
| | | <el-form :model="form" label-width="200px" label-position="left" :rules="rules" ref="formRef" style="margin-top: 20px"> |
| | | <el-form :model="form" |
| | | label-width="200px" |
| | | label-position="left" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | style="margin-top: 20px"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="姓名:" prop="staffOnJobId"> |
| | | <el-form-item label="姓名:" |
| | | prop="staffOnJobId"> |
| | | <el-select v-model="form.staffOnJobId" |
| | | placeholder="请选择人员" |
| | | style="width: 100%" |
| | | :disabled="operationType === 'edit'" |
| | | @change="handleSelect"> |
| | | <el-option |
| | | v-for="item in personList" |
| | | <el-option v-for="item in personList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | | :value="item.id" |
| | | /> |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | {{ currentStaffRecord.sex || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="户籍住址:"> |
| | | {{ currentStaffRecord.nativePlace || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="岗位:"> |
| | | {{ currentStaffRecord.postName || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="现住址:"> |
| | | {{ currentStaffRecord.adress || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="离职日期:" prop="leaveDate"> |
| | | <el-date-picker |
| | | v-model="form.leaveDate" |
| | | <el-form-item label="离职日期:" |
| | | prop="leaveDate"> |
| | | <el-date-picker v-model="form.leaveDate" |
| | | type="date" |
| | | :disabled="operationType === 'edit'" |
| | | :disabled-date="disabledFutureDate" |
| | | placeholder="请选择离职日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="离职原因:" prop="reason"> |
| | | <el-select v-model="form.reason" placeholder="请选择离职原因" style="width: 100%" @change="handleSelectDimissionReason"> |
| | | <el-option |
| | | v-for="(item, index) in dimissionReasonOptions" |
| | | <el-form-item label="离职原因:" |
| | | prop="reason"> |
| | | <el-select v-model="form.reason" |
| | | placeholder="请选择离职原因" |
| | | style="width: 100%" |
| | | @change="handleSelectDimissionReason"> |
| | | <el-option v-for="(item, index) in dimissionReasonOptions" |
| | | :key="index" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="备注:" prop="remark" v-if="form.reason === 'other'"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | <el-form-item label="备注:" |
| | | prop="remark" |
| | | v-if="form.reason === 'other'"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="备注" |
| | | maxlength="500" |
| | | show-word-limit |
| | | /> |
| | | show-word-limit /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | |
| | | <!-- <el-row :gutter="30">--> |
| | | <!-- <el-col :span="12">--> |
| | | <!-- <div class="info-item">--> |
| | |
| | | <!-- </el-col>--> |
| | | <!-- </el-row>--> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | <script setup> |
| | | import {ref, reactive, toRefs, getCurrentInstance} from "vue"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import {createStaffLeave, updateStaffLeave} from "@/api/personnelManagement/staffLeave.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | import { |
| | | createStaffLeave, |
| | | updateStaffLeave, |
| | | } from "@/api/personnelManagement/staffLeave.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const operationType = ref(""); |
| | | const getTodayDate = () => { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = `${now.getMonth() + 1}`.padStart(2, '0'); |
| | | const day = `${now.getDate()}`.padStart(2, '0'); |
| | | const month = `${now.getMonth() + 1}`.padStart(2, "0"); |
| | | const day = `${now.getDate()}`.padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | }; |
| | | |
| | | const disabledFutureDate = (time) => { |
| | | const disabledFutureDate = time => { |
| | | const todayEnd = new Date(); |
| | | todayEnd.setHours(23, 59, 59, 999); |
| | | return time.getTime() > todayEnd.getTime(); |
| | |
| | | }, |
| | | rules: { |
| | | staffName: [{ required: true, message: "请选择人员" }], |
| | | leaveDate: [{ required: true, message: "请选择离职日期", trigger: "change" }], |
| | | leaveDate: [ |
| | | { required: true, message: "请选择离职日期", trigger: "change" }, |
| | | ], |
| | | reason: [{ required: true, message: "请选择离职原因"}], |
| | | }, |
| | | dimissionReasonOptions: [ |
| | | {label: '薪资待遇', value: 'salary'}, |
| | | {label: '职业发展', value: 'career_development'}, |
| | | {label: '工作环境', value: 'work_environment'}, |
| | | {label: '个人原因', value: 'personal_reason'}, |
| | | {label: '其他', value: 'other'}, |
| | | { label: "薪资待遇", value: "salary" }, |
| | | { label: "职业发展", value: "career_development" }, |
| | | { label: "工作环境", value: "work_environment" }, |
| | | { label: "个人原因", value: "personal_reason" }, |
| | | { label: "其他", value: "other" }, |
| | | ], |
| | | currentStaffRecord: {}, |
| | | }); |
| | | const { form, rules, dimissionReasonOptions, currentStaffRecord } = toRefs(data); |
| | | const { form, rules, dimissionReasonOptions, currentStaffRecord } = |
| | | toRefs(data); |
| | | |
| | | // 打开弹框 |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | if (operationType.value === 'edit') { |
| | | currentStaffRecord.value = row |
| | | form.value.staffOnJobId = row.staffOnJobId |
| | | form.value.leaveDate = row.leaveDate |
| | | form.value.reason = row.reason |
| | | form.value.remark = row.remark |
| | | if (operationType.value === "edit") { |
| | | currentStaffRecord.value = row; |
| | | form.value.staffOnJobId = row.staffOnJobId; |
| | | form.value.leaveDate = row.leaveDate; |
| | | form.value.reason = row.reason; |
| | | form.value.remark = row.remark; |
| | | personList.value = [ |
| | | { |
| | | staffName: row.staffName, |
| | | id: row.staffOnJobId, |
| | | } |
| | | ] |
| | | }, |
| | | ]; |
| | | } else { |
| | | form.value.leaveDate = getTodayDate() |
| | | getList() |
| | | form.value.leaveDate = getTodayDate(); |
| | | getList(); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleSelectDimissionReason = (val) => { |
| | | if (val === 'other') { |
| | | form.value.remark = '' |
| | | const handleSelectDimissionReason = val => { |
| | | if (val === "other") { |
| | | form.value.remark = ""; |
| | | } |
| | | } |
| | | }; |
| | | // 提交产品表单 |
| | | const submitForm = () => { |
| | | form.value.staffState = 0 |
| | | if (form.value.reason !== 'other') { |
| | | form.value.remark = '' |
| | | form.value.staffState = 0; |
| | | if (form.value.reason !== "other") { |
| | | form.value.remark = ""; |
| | | } |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | |
| | | createStaffLeave(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }) |
| | | }); |
| | | } else { |
| | | updateStaffLeave(currentStaffRecord.value.id, form.value).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }) |
| | | }); |
| | | } |
| | | } |
| | | }) |
| | | |
| | | } |
| | | }); |
| | | }; |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | // 表单已注释,手动重置表单数据 |
| | |
| | | remark: "", |
| | | }; |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | emit("close"); |
| | | }; |
| | | |
| | | const personList = ref([]); |
| | |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1 |
| | | staffState: 1, |
| | | }).then(res => { |
| | | personList.value = res.data.records || [] |
| | | }) |
| | | personList.value = res.data.records || []; |
| | | }); |
| | | }; |
| | | |
| | | const handleSelect = (val) => { |
| | | let obj = personList.value.find(item => item.id === val) |
| | | currentStaffRecord.value = {} |
| | | const handleSelect = val => { |
| | | let obj = personList.value.find(item => item.id === val); |
| | | currentStaffRecord.value = {}; |
| | | if (obj) { |
| | | // 保留离职日期和离职原因,只更新员工信息 |
| | | currentStaffRecord.value = obj |
| | | currentStaffRecord.value = obj; |
| | | } |
| | | } |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">姓名:</span> |
| | | <el-input |
| | | v-model="searchForm.staffName" |
| | | <el-input v-model="searchForm.staffName" |
| | | style="width: 240px" |
| | | placeholder="请输入姓名搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >搜索</el-button |
| | | > |
| | | :prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">搜索</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">新增离职</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增离职</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | label: "状态", |
| | | prop: "staffState", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | if (params == 0) { |
| | | return "离职"; |
| | | } else if (params == 1) { |
| | |
| | | return null; |
| | | } |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | if (params == 0) { |
| | | return "danger"; |
| | | } else if (params == 1) { |
| | |
| | | prop: "sex", |
| | | }, |
| | | { |
| | | label: "户籍住址", |
| | | prop: "nativePlace", |
| | | }, |
| | | { |
| | | label: "部门", |
| | | prop: "deptName", |
| | | }, |
| | |
| | | prop: "postName", |
| | | }, |
| | | { |
| | | label: "现住址", |
| | | prop: "adress", |
| | | width:200 |
| | | }, |
| | | { |
| | | label: "第一学历", |
| | | prop: "firstStudy", |
| | | }, |
| | | { |
| | | label: "专业", |
| | | prop: "profession", |
| | | width:100 |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "年龄", |
| | |
| | | { |
| | | label: "联系电话", |
| | | prop: "phone", |
| | | width:150 |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "紧急联系人", |
| | | prop: "emergencyContact", |
| | | width: 120 |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "紧急联系人电话", |
| | | prop: "emergencyContactPhone", |
| | | width:150 |
| | | width: 150, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | align: "center", |
| | | fixed: 'right', |
| | | fixed: "right", |
| | | operation: [ |
| | | { |
| | | name: "编辑", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | const formDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | findStaffLeaveListPage({...page, ...searchForm.value}).then(res => { |
| | | findStaffLeaveListPage({ ...page, ...searchForm.value }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // 表格选择数据 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // 打开弹框 |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | formDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | |
| | | // 导出 |
| | |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">姓名:</span> |
| | | <el-input v-model="searchForm.staffName" style="width: 240px" placeholder="请输入姓名搜索" @change="handleQuery" |
| | | clearable :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" class="search_title">合同结束日期:</span> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="请选择" clearable @change="changeDaterange" /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button> |
| | | <el-input v-model="searchForm.staffName" |
| | | style="width: 240px" |
| | | placeholder="请输入姓名搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" |
| | | class="search_title">合同结束日期:</span> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="changeDaterange" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">搜索</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true" |
| | | @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | <!-- 合同导入对话框 --> |
| | | <el-dialog |
| | | :title="upload.title" |
| | | <el-dialog :title="upload.title" |
| | | v-model="upload.open" |
| | | width="400px" |
| | | append-to-body |
| | | > |
| | | <el-upload |
| | | ref="uploadRef" |
| | | append-to-body> |
| | | <el-upload ref="uploadRef" |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | |
| | | :on-progress="handleFileUploadProgress" |
| | | :on-success="handleFileSuccess" |
| | | :auto-upload="false" |
| | | drag |
| | | > |
| | | drag> |
| | | <el-icon class="el-icon--upload"><upload-filled /></el-icon> |
| | | <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> |
| | | <template #tip> |
| | |
| | | </el-upload> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitFileForm">确 定</el-button> |
| | | <el-button type="primary" |
| | | @click="submitFileForm">确 定</el-button> |
| | | <el-button @click="upload.open = false">取 消</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | label: "状态", |
| | | prop: "staffState", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | if (params == 0) { |
| | | return "离职"; |
| | | } else if (params == 1) { |
| | |
| | | return null; |
| | | } |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | if (params == 0) { |
| | | return "danger"; |
| | | } else if (params == 1) { |
| | |
| | | prop: "sex", |
| | | }, |
| | | { |
| | | label: "户籍住址", |
| | | prop: "nativePlace", |
| | | }, |
| | | { |
| | | label: "岗位", |
| | | prop: "postName", |
| | | }, |
| | | { |
| | | label: "现住址", |
| | | prop: "adress", |
| | | width: 200 |
| | | }, |
| | | { |
| | | label: "第一学历", |
| | |
| | | { |
| | | label: "专业", |
| | | prop: "profession", |
| | | width: 100 |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "年龄", |
| | |
| | | { |
| | | label: "联系电话", |
| | | prop: "phone", |
| | | width: 150 |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "紧急联系人", |
| | | prop: "emergencyContact", |
| | | width: 120 |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "紧急联系人电话", |
| | | prop: "emergencyContactPhone", |
| | | width: 150 |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "合同结束日期", |
| | | prop: "contractExpireTime", |
| | | width: 120 |
| | | width: 120, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | align: "center", |
| | | fixed: 'right', |
| | | fixed: "right", |
| | | width: 120, |
| | | operation: [ |
| | | { |
| | | name: "详情", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | } |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const filesDia = ref() |
| | | const filesDia = ref(); |
| | | const tableData = ref([]); |
| | | const selectedRows = ref([]); |
| | | const tableLoading = ref(false); |
| | |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | const formDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const changeDaterange = (value) => { |
| | | const changeDaterange = value => { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | if (value) { |
| | |
| | | getList(); |
| | | }; |
| | | // 打开附件弹框 |
| | | const openFilesFormDia = (row) => { |
| | | const openFilesFormDia = row => { |
| | | nextTick(() => { |
| | | filesDia.value?.openDialog( row,'合同') |
| | | }) |
| | | filesDia.value?.openDialog(row, "合同"); |
| | | }); |
| | | }; |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | params.staffState = 1 |
| | | staffOnJobListPage(params).then(res => { |
| | | params.entryDate = undefined; |
| | | params.staffState = 1; |
| | | staffOnJobListPage(params) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // 表格选择数据 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // 打开弹框 |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | formDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | // 导出 |
| | | const handleOut = () => { |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/staff/staffOnJob/export", {staffState: 1}, "合同管理.xlsx"); |
| | | proxy.download( |
| | | "/staff/staffOnJob/export", |
| | | { staffState: 1 }, |
| | | "合同管理.xlsx" |
| | | ); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | |
| | | :fields="submitFormFields" |
| | | :active-template="activeTemplate" |
| | | :user-options="flowUserOptions" |
| | | :module-key="APPROVAL_MODULE_KEYS.TRANSFER" |
| | | :is-edit="isSubmitEdit" |
| | | :saving="submitSaving" |
| | | :form-ref="submitFormRef" |
| | |
| | | :fields="submitFormFields" |
| | | :active-template="activeTemplate" |
| | | :user-options="flowUserOptions" |
| | | :module-key="APPROVAL_MODULE_KEYS.WORK_HANDOVER" |
| | | :is-edit="isSubmitEdit" |
| | | :saving="submitSaving" |
| | | :form-ref="submitFormRef" |
| | |
| | | <el-option v-for="item in shifts_list" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" /> |
| | | :value="item.value" |
| | | :disabled=" |
| | | operationType === 'add' && |
| | | (String(item.label || '').includes('假') || |
| | | String(item.label || '').includes('休')) |
| | | " /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="打卡范围(m)" |
| | | prop="radius"> |
| | | prop="radius" |
| | | v-show="!hideClockFields"> |
| | | <el-input-number v-model="form.radius" |
| | | :min="10" |
| | | :max="1000" |
| | |
| | | </el-form-item> |
| | | <!-- 高德地图选择 --> |
| | | <el-form-item label="打卡位置" |
| | | prop="longitude"> |
| | | prop="longitude" |
| | | v-show="!hideClockFields"> |
| | | <div class="map-container"> |
| | | <div class="map-header" |
| | | style="margin-bottom: 10px"> |
| | |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="地点名称" |
| | | prop="locationName"> |
| | | prop="locationName" |
| | | v-show="!hideClockFields"> |
| | | <el-input v-model="form.locationName" |
| | | :disabled="operationType === 'view'" |
| | | placeholder="请输入地点名称" /> |
| | | </el-form-item> |
| | | <!-- 上下班时间 --> |
| | | <el-form-item label="上班时间" |
| | | prop="startAt"> |
| | | prop="startAt" |
| | | v-show="!hideClockFields"> |
| | | <el-time-picker v-model="form.startAt" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | |
| | | :disabled="operationType === 'view'" /> |
| | | </el-form-item> |
| | | <el-form-item label="下班时间" |
| | | prop="endAt"> |
| | | prop="endAt" |
| | | v-show="!hideClockFields"> |
| | | <el-time-picker v-model="form.endAt" |
| | | format="HH:mm" |
| | | value-format="HH:mm" |
| | |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm" v-if="operationType !== 'view'">确定</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm" |
| | | v-if="operationType !== 'view'">确定</el-button> |
| | | <el-button @click="dialogVisible = false">取消</el-button> |
| | | </span> |
| | | </template> |
| | |
| | | // 获取班次字典值 |
| | | const { shifts_list } = useDict("shifts_list"); |
| | | |
| | | const selectedShiftLabel = computed(() => { |
| | | const item = shifts_list.value?.find( |
| | | s => String(s.value) === String(form.shift) |
| | | ); |
| | | return item?.label || ""; |
| | | }); |
| | | |
| | | const hideClockFields = computed(() => { |
| | | if (props.operationType === "view") return false; |
| | | return ( |
| | | selectedShiftLabel.value.includes("假") || |
| | | selectedShiftLabel.value.includes("休") |
| | | ); |
| | | }); |
| | | |
| | | // 表单数据 |
| | | const formRef = ref(); |
| | | const form = reactive({ |
| | |
| | | }); |
| | | |
| | | // 表单验证规则 |
| | | const rules = { |
| | | const rules = computed(() => { |
| | | const base = { |
| | | sysDeptId: [{ required: true, message: "请选择部门", trigger: "change" }], |
| | | shift: [{ required: true, message: "请选择班次", trigger: "change" }], |
| | | }; |
| | | |
| | | if (hideClockFields.value) { |
| | | return base; |
| | | } |
| | | |
| | | return { |
| | | ...base, |
| | | locationName: [ |
| | | { required: true, message: "请输入地点名称", trigger: "blur" }, |
| | | ], |
| | | longitude: [{ required: true, message: "请选择打卡位置", trigger: "blur" }], |
| | | latitude: [{ required: true, message: "请选择打卡位置", trigger: "blur" }], |
| | | shift: [{ required: true, message: "请选择班次", trigger: "change" }], |
| | | radius: [{ required: true, message: "请输入打卡范围", trigger: "blur" }], |
| | | startAt: [{ required: true, message: "请选择上班时间", trigger: "change" }], |
| | | endAt: [ |
| | | { required: true, message: "请选择下班时间", trigger: "change" }, |
| | | { |
| | | validator: (rule, value, callback) => { |
| | | // 如果是夜班,不限制时间顺序 |
| | | if (form.shift && ['夜班', '夜'].includes(form.shift)) { |
| | | if (form.shift && ["夜班", "夜"].includes(form.shift)) { |
| | | callback(); |
| | | return; |
| | | } |
| | |
| | | }, |
| | | ], |
| | | }; |
| | | }); |
| | | |
| | | // 部门选项 |
| | | const deptOptions = ref([]); |
| | |
| | | watch( |
| | | () => form.radius, |
| | | newValue => { |
| | | if (circle) { |
| | | circle.setRadius(newValue); |
| | | if (circle && newValue != null && newValue !== "") { |
| | | circle.setRadius(Number(newValue)); |
| | | } |
| | | } |
| | | ); |
| | |
| | | () => { |
| | | if (formRef.value && form.endAt) { |
| | | formRef.value.validateField("endAt"); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => hideClockFields.value, |
| | | val => { |
| | | if (!val) return; |
| | | form.radius = null; |
| | | form.longitude = ""; |
| | | form.latitude = ""; |
| | | form.locationName = ""; |
| | | form.startAt = ""; |
| | | form.endAt = ""; |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate([ |
| | | "radius", |
| | | "longitude", |
| | | "latitude", |
| | | "locationName", |
| | | "startAt", |
| | | "endAt", |
| | | ]); |
| | | } |
| | | } |
| | | ); |
| | |
| | | |
| | | // 初始化地图 |
| | | setTimeout(() => { |
| | | if (!hideClockFields.value) { |
| | | initMap(); |
| | | } |
| | | }, 100); |
| | | } |
| | | } |
| | |
| | | </el-button> |
| | | </div> |
| | | </div> |
| | | <!-- 查询条件 --> |
| | | <!-- <el-form :model="searchForm" |
| | | <el-form :model="searchForm" |
| | | :inline="true" |
| | | class="search-form mb16"> |
| | | <el-form-item label="部门:" |
| | | prop="countId"> |
| | | <el-tree-select v-model="searchForm.countId" |
| | | prop="sysDeptId"> |
| | | <el-tree-select v-model="searchForm.sysDeptId" |
| | | :data="deptOptions" |
| | | :props="{ value: 'id', label: 'label', children: 'children' }" |
| | | value-key="id" |
| | | placeholder="请选择部门" |
| | | check-strictly |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item label="地点:" |
| | | prop="locationName"> |
| | | <el-input v-model="searchForm.locationName" |
| | | placeholder="请输入地点名称" |
| | | clearable |
| | | style="width: 200px" /> |
| | | </el-form-item> |
| | | <el-form-item label="班次:" |
| | | prop="shift"> |
| | | <el-select v-model="searchForm.shift" |
| | | placeholder="请选择班次" |
| | | clearable |
| | | style="width: 200px"> |
| | | <el-option |
| | | v-for="item in shifts_list" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" |
| | | @click="fetchData"> |
| | | @click="handleSearch"> |
| | | <el-icon> |
| | | <Search /> |
| | | </el-icon> |
| | |
| | | 重置 |
| | | </el-button> |
| | | </el-form-item> |
| | | </el-form> --> |
| | | </el-form> |
| | | <!-- 班次列表 --> |
| | | <el-card shadow="never" |
| | | class="mb16"> |
| | |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="locationName" |
| | | label="地点名称" /> |
| | | label="地点名称"> |
| | | <template #default="scope"> |
| | | {{ scope.row.locationName || "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="longitude" |
| | | label="经度" /> |
| | | label="经度"> |
| | | <template #default="scope"> |
| | | {{ scope.row.longitude ?? "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="latitude" |
| | | label="纬度" /> |
| | | label="纬度"> |
| | | <template #default="scope"> |
| | | {{ scope.row.latitude ?? "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="radius" |
| | | label="打卡范围(m)" /> |
| | | label="打卡范围(m)"> |
| | | <template #default="scope"> |
| | | {{ scope.row.radius ?? "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="startAt" |
| | | label="上班时间"> |
| | | <template #default="scope"> |
| | | {{ scope.row.startAt }} |
| | | {{ scope.row.startAt || "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="endAt" |
| | | label="下班时间"> |
| | | <template #default="scope"> |
| | | {{ scope.row.endAt }} |
| | | {{ scope.row.endAt || "-" }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted } from "vue"; |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { |
| | | Plus, |
| | |
| | | |
| | | // 查询表单 |
| | | const searchForm = reactive({ |
| | | countId: "", |
| | | locationName: "", |
| | | sysDeptId: "", |
| | | shift: "", |
| | | }); |
| | | |
| | | // 部门选项 |
| | |
| | | tableLoading.value = true; |
| | | getAttendanceRules({ ...page, ...searchForm }) |
| | | .then(res => { |
| | | tableData.value = res.data.records; |
| | | const records = Array.isArray(res?.data?.records) ? res.data.records : []; |
| | | tableData.value = records.filter(r => String(r?.id) !== "999"); |
| | | page.total = res.data.total; |
| | | }) |
| | | .finally(() => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | const handleSearch = () => { |
| | | page.current = 1; |
| | | fetchData(); |
| | | }; |
| | | |
| | | // 分页变更 |
| | |
| | | |
| | | // 重置搜索 |
| | | const resetSearch = () => { |
| | | searchForm.countId = ""; |
| | | searchForm.locationName = ""; |
| | | searchForm.sysDeptId = ""; |
| | | searchForm.shift = ""; |
| | | page.current = 1; |
| | | fetchData(); |
| | | }; |
| | | |
| | |
| | | 'shift-box-early': m.shift === '早班', |
| | | 'shift-box-mid': m.shift === '中班', |
| | | 'shift-box-night': m.shift === '夜班', |
| | | 'shift-box-rest': m.shift === '休息', |
| | | 'shift-box-rest': !m.shift || m.shift === '休息', |
| | | 'shift-box-leave': m.shift === '请假', |
| | | 'shift-box-other': m.shift === '夜11', |
| | | 'shift-box-business': m.shift === '夜12', |
| | | }"> |
| | | <span class="shift-text">{{ getShiftNameByValue(m.shift) || '—' }}</span> |
| | | <span class="shift-text">{{ getShiftNameByValue(m.shift) || '休息' }}</span> |
| | | </div> |
| | | <template #dropdown> |
| | | <el-dropdown-menu> |
| | |
| | | </div> --> |
| | | <div class="user-total"> |
| | | <span class="total-label">合计出勤:</span> |
| | | <span class="total-value">{{ item.work_time }}天</span> |
| | | <span class="total-value">{{ item.year.totalYearAttendance }}天</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <div class="search_thing"> |
| | | <div class="search_label" |
| | | style="width: 90px"> |
| | | <span style="color: red; margin-right: 4px">*</span>部门: |
| | | </div> |
| | | <div class="search_input"> |
| | | <el-tree-select v-model="schedulingQuery.deptId" |
| | | :data="deptOptions" |
| | | :props="{ value: 'id', label: 'label', children: 'children' }" |
| | | value-key="id" |
| | | placeholder="请选择部门" |
| | | check-strictly |
| | | clearable |
| | | @change="onSchedulingDeptChange" |
| | | style="width: 100%" /> |
| | | </div> |
| | | </div> |
| | | <div class="search_thing"> |
| | | <div class="search_label" |
| | | style="width: 90px"> |
| | | <span style="color: red; margin-right: 4px">*</span>人员名称: |
| | | </div> |
| | | <div class="search_input"> |
| | |
| | | style="width: 100%" |
| | | multiple |
| | | clearable |
| | | collapse-tags> |
| | | collapse-tags |
| | | :disabled="!schedulingQuery.deptId"> |
| | | <el-option v-for="item in personList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | |
| | | <div class="search_input"> |
| | | <el-select v-model="schedulingQuery.shift" |
| | | placeholder="请选择" |
| | | style="width: 100%"> |
| | | <el-option v-for="item in classType" |
| | | style="width: 100%" |
| | | :disabled="!schedulingQuery.deptId"> |
| | | <el-option v-for="item in schedulingClassType" |
| | | :key="item.id" |
| | | :label="getShiftNameByValue(item.shift)" |
| | | :value="item.id"> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { |
| | | ref, |
| | | reactive, |
| | | computed, |
| | | watch, |
| | | onMounted, |
| | | getCurrentInstance, |
| | | } from "vue"; |
| | | import { useRouter } from "vue-router"; |
| | | import { |
| | | page, |
| | |
| | | |
| | | // 人员列表 |
| | | const personList = ref([]); |
| | | const personListAll = ref([]); |
| | | |
| | | // 加载状态 |
| | | const loading = ref(false); |
| | |
| | | // 排班查询条件 |
| | | const schedulingQuery = reactive({ |
| | | week: "", |
| | | deptId: "", |
| | | userId: null, |
| | | shift: "", |
| | | }); |
| | | |
| | | const schedulingClassType = computed(() => { |
| | | if (!schedulingQuery.deptId) return classType.value || []; |
| | | return (classType.value || []).filter(item => { |
| | | if (["998", "999"].includes(String(item?.id))) return true; |
| | | const itemDeptId = item.deptId ?? item.sysDeptId ?? item.dept_id; |
| | | return String(itemDeptId) === String(schedulingQuery.deptId); |
| | | }); |
| | | }); |
| | | |
| | | // 列表数据 |
| | |
| | | proxy.$modal.msgError("请选择周次"); |
| | | return; |
| | | } |
| | | if (!schedulingQuery.deptId) { |
| | | proxy.$modal.msgError("请选择部门"); |
| | | return; |
| | | } |
| | | let time = schedulingQuery.week.getTime(); |
| | | |
| | | // 格式化日期为 YYYY-MM-DD 格式 |
| | |
| | | proxy.$modal.msgSuccess("操作成功"); |
| | | schedulingVisible.value = false; |
| | | schedulingQuery.week = ""; |
| | | schedulingQuery.deptId = ""; |
| | | schedulingQuery.userId = null; |
| | | schedulingQuery.shift = ""; |
| | | personList.value = []; |
| | | refresh(); |
| | | }) |
| | | .catch(err => { |
| | |
| | | } |
| | | proxy.$download.saveAs(blob, fileName + ".xlsx"); |
| | | }) |
| | | .catch(err => { |
| | | }) |
| | | .catch(err => {}) |
| | | .finally(() => { |
| | | downLoading.value = false; |
| | | }); |
| | |
| | | // 查询规则列表 |
| | | const fetchData = () => { |
| | | getAttendanceRules({ current: -1, size: -1 }).then(res => { |
| | | classType.value = res.data.records; |
| | | const records = Array.isArray(res?.data?.records) ? res.data.records : []; |
| | | const appendList = [ |
| | | { id: 998, shift: "休息" }, |
| | | { id: 999, shift: "请假" }, |
| | | ]; |
| | | const exists = new Set(records.map(r => String(r?.id))); |
| | | appendList.forEach(item => { |
| | | if (!exists.has(String(item.id))) { |
| | | records.push(item); |
| | | } |
| | | }); |
| | | classType.value = records; |
| | | }); |
| | | }; |
| | | // 获取用户 |
| | |
| | | staffState: 1, |
| | | }).then(res => { |
| | | let arr = res.data.records; |
| | | personList.value = arr; |
| | | personListAll.value = arr; |
| | | updatePersonListByDept(); |
| | | }); |
| | | }; |
| | | |
| | | const findDeptNodeById = (deptId, deptList = deptOptions.value) => { |
| | | const list = Array.isArray(deptList) ? deptList : []; |
| | | for (const node of list) { |
| | | if (String(node.id) === String(deptId)) return node; |
| | | if (node.children && node.children.length) { |
| | | const found = findDeptNodeById(deptId, node.children); |
| | | if (found) return found; |
| | | } |
| | | } |
| | | return null; |
| | | }; |
| | | |
| | | const collectDeptIds = node => { |
| | | const ids = []; |
| | | if (!node) return ids; |
| | | ids.push(String(node.id)); |
| | | if (node.children && node.children.length) { |
| | | node.children.forEach(child => { |
| | | ids.push(...collectDeptIds(child)); |
| | | }); |
| | | } |
| | | return ids; |
| | | }; |
| | | |
| | | const updatePersonListByDept = () => { |
| | | if (!schedulingQuery.deptId) { |
| | | personList.value = []; |
| | | return; |
| | | } |
| | | const node = findDeptNodeById(schedulingQuery.deptId); |
| | | const deptIds = new Set(collectDeptIds(node)); |
| | | personList.value = (personListAll.value || []).filter(item => { |
| | | const itemDeptId = item.deptId ?? item.sysDeptId ?? item.dept_id; |
| | | return deptIds.has(String(itemDeptId)); |
| | | }); |
| | | }; |
| | | |
| | | const setDefaultShiftByDept = () => { |
| | | if (!schedulingQuery.deptId) { |
| | | schedulingQuery.shift = ""; |
| | | return; |
| | | } |
| | | const first = schedulingClassType.value?.[0]; |
| | | schedulingQuery.shift = first ? first.id : ""; |
| | | }; |
| | | |
| | | const onSchedulingDeptChange = () => { |
| | | schedulingQuery.userId = null; |
| | | updatePersonListByDept(); |
| | | setDefaultShiftByDept(); |
| | | }; |
| | | |
| | | // 根据字典获取日期 |
| | |
| | | } |
| | | monthList.value.reverse(); |
| | | }); |
| | | |
| | | watch( |
| | | () => classType.value, |
| | | () => { |
| | | if (schedulingVisible.value && schedulingQuery.deptId) { |
| | | setDefaultShiftByDept(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => schedulingVisible.value, |
| | | val => { |
| | | if (!val) return; |
| | | schedulingQuery.week = ""; |
| | | schedulingQuery.deptId = ""; |
| | | schedulingQuery.userId = null; |
| | | schedulingQuery.shift = ""; |
| | | personList.value = []; |
| | | } |
| | | ); |
| | | </script> |
| | | |
| | | <style scoped> |
| | |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">姓名:</span> |
| | | <el-input v-model="searchForm.staffName" style="width: 240px" placeholder="请输入姓名搜索" @change="handleQuery" |
| | | clearable :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" class="search_title">合同结束日期:</span> |
| | | <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange" |
| | | placeholder="请选择" clearable @change="changeDaterange" /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button> |
| | | <el-input v-model="searchForm.staffName" |
| | | style="width: 240px" |
| | | placeholder="请输入姓名搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" /> |
| | | <span style="margin-left: 10px" |
| | | class="search_title">合同结束日期:</span> |
| | | <el-date-picker v-model="searchForm.entryDate" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | type="daterange" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="changeDaterange" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">搜索</el-button> |
| | | </div> |
| | | <div> |
| | | <!-- <el-button type="primary" @click="openForm('add')">新增入职</el-button>--> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true" |
| | | @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | | :isSelection="true" |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | <renew-contract |
| | | v-if="isShowRenewContractModal" |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | <renew-contract v-if="isShowRenewContractModal" |
| | | v-model:visible="isShowRenewContractModal" |
| | | :id="id" |
| | | @completed="handleQuery" |
| | | /> |
| | | |
| | | @completed="handleQuery" /> |
| | | <!-- 合同导入对话框 --> |
| | | <el-dialog |
| | | :title="upload.title" |
| | | <el-dialog :title="upload.title" |
| | | v-model="upload.open" |
| | | width="400px" |
| | | append-to-body |
| | | > |
| | | <el-upload |
| | | ref="uploadRef" |
| | | append-to-body> |
| | | <el-upload ref="uploadRef" |
| | | :limit="1" |
| | | accept=".xlsx, .xls" |
| | | :headers="upload.headers" |
| | |
| | | :on-progress="handleFileUploadProgress" |
| | | :on-success="handleFileSuccess" |
| | | :auto-upload="false" |
| | | drag |
| | | > |
| | | drag> |
| | | <el-icon class="el-icon--upload"><upload-filled /></el-icon> |
| | | <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div> |
| | | <template #tip> |
| | |
| | | </el-upload> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitFileForm">确 定</el-button> |
| | | <el-button type="primary" |
| | | @click="submitFileForm">确 定</el-button> |
| | | <el-button @click="upload.open = false">取 消</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import { onMounted, ref, defineAsyncComponent } from "vue"; |
| | | import FormDia from "@/views/personnelManagement/contractManagement/components/formDia.vue"; |
| | | const RenewContract = defineAsyncComponent(() => import("@/views/personnelManagement/employeeRecord/components/RenewContract.vue")); |
| | | const RenewContract = defineAsyncComponent(() => |
| | | import( |
| | | "@/views/personnelManagement/employeeRecord/components/RenewContract.vue" |
| | | ) |
| | | ); |
| | | import { ElMessageBox } from "element-plus"; |
| | | import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js"; |
| | | import dayjs from "dayjs"; |
| | |
| | | label: "状态", |
| | | prop: "staffState", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | if (params == 0) { |
| | | return "离职"; |
| | | } else if (params == 1) { |
| | |
| | | return null; |
| | | } |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | if (params == 0) { |
| | | return "danger"; |
| | | } else if (params == 1) { |
| | |
| | | prop: "sex", |
| | | }, |
| | | { |
| | | label: "户籍住址", |
| | | prop: "nativePlace", |
| | | }, |
| | | { |
| | | label: "岗位", |
| | | prop: "postName", |
| | | }, |
| | | { |
| | | label: "现住址", |
| | | prop: "adress", |
| | | width: 200 |
| | | }, |
| | | { |
| | | label: "第一学历", |
| | |
| | | { |
| | | label: "专业", |
| | | prop: "profession", |
| | | width: 100 |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "年龄", |
| | |
| | | { |
| | | label: "联系电话", |
| | | prop: "phone", |
| | | width: 150 |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "紧急联系人", |
| | | prop: "emergencyContact", |
| | | width: 120 |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "紧急联系人电话", |
| | | prop: "emergencyContactPhone", |
| | | width: 150 |
| | | width: 150, |
| | | }, |
| | | // { |
| | | // label: "合同年限", |
| | |
| | | { |
| | | label: "合同结束日期", |
| | | prop: "contractExpireTime", |
| | | width: 120 |
| | | width: 120, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | align: "center", |
| | | fixed: 'right', |
| | | fixed: "right", |
| | | width: 160, |
| | | operation: [ |
| | | { |
| | | name: "详情", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | |
| | | name: "续签合同", |
| | | type: "text", |
| | | showHide: row => row.staffState === 1, |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | isShowRenewContractModal.value = true; |
| | | id.value = row.id; |
| | | }, |
| | | } |
| | | }, |
| | | ], |
| | | }, |
| | | ]); |
| | | const filesDia = ref() |
| | | const filesDia = ref(); |
| | | const isShowRenewContractModal = ref(false); |
| | | const id = ref(0); |
| | | const tableData = ref([]); |
| | |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | const formDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | const changeDaterange = (value) => { |
| | | const changeDaterange = value => { |
| | | searchForm.value.entryDateStart = undefined; |
| | | searchForm.value.entryDateEnd = undefined; |
| | | if (value) { |
| | |
| | | getList(); |
| | | }; |
| | | // 打开附件弹框 |
| | | const openFilesFormDia = (row) => { |
| | | console.log(row) |
| | | const openFilesFormDia = row => { |
| | | console.log(row); |
| | | nextTick(() => { |
| | | filesDia.value?.openDialog( row,'合同') |
| | | }) |
| | | filesDia.value?.openDialog(row, "合同"); |
| | | }); |
| | | }; |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | const params = { ...searchForm.value, ...page }; |
| | | params.entryDate = undefined |
| | | params.staffState = 1 |
| | | staffOnJobListPage(params).then(res => { |
| | | params.entryDate = undefined; |
| | | params.staffState = 1; |
| | | staffOnJobListPage(params) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // 表格选择数据 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // 打开弹框 |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | formDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | // 导出 |
| | | const handleOut = () => { |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | proxy.download("/staff/staffOnJob/export", {staffState: 1}, "合同管理.xlsx"); |
| | | proxy.download( |
| | | "/staff/staffOnJob/export", |
| | | { staffState: 1 }, |
| | | "合同管理.xlsx" |
| | | ); |
| | | }) |
| | | .catch(() => { |
| | | proxy.$modal.msg("已取消"); |
| | |
| | | } |
| | | /** 提交上传文件 */ |
| | | function submitFileForm() { |
| | | console.log(upload.url + '?updateSupport=' + upload.updateSupport) |
| | | console.log(upload.url + "?updateSupport=" + upload.updateSupport); |
| | | proxy.$refs["uploadRef"].submit(); |
| | | } |
| | | /**文件上传中处理 */ |
| | |
| | | <template> |
| | | <div> |
| | | <el-dialog |
| | | v-model="dialogFormVisible" |
| | | <el-dialog v-model="dialogFormVisible" |
| | | :title="operationType === 'add' ? '新增离职' : '编辑离职'" |
| | | width="70%" |
| | | @close="closeDia" |
| | | > |
| | | @close="closeDia"> |
| | | <!-- 员工信息展示区域 --> |
| | | <div class="info-section"> |
| | | <div class="info-title">员工信息</div> |
| | | <el-form :model="form" label-width="200px" label-position="left" :rules="rules" ref="formRef" style="margin-top: 20px"> |
| | | <el-form :model="form" |
| | | label-width="200px" |
| | | label-position="left" |
| | | :rules="rules" |
| | | ref="formRef" |
| | | style="margin-top: 20px"> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="姓名:" prop="staffOnJobId"> |
| | | <el-form-item label="姓名:" |
| | | prop="staffOnJobId"> |
| | | <el-select v-model="form.staffOnJobId" |
| | | placeholder="请选择人员" |
| | | style="width: 100%" |
| | | :disabled="operationType === 'edit'" |
| | | @change="handleSelect"> |
| | | <el-option |
| | | v-for="item in personList" |
| | | <el-option v-for="item in personList" |
| | | :key="item.id" |
| | | :label="item.staffName" |
| | | :value="item.id" |
| | | /> |
| | | :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | {{ currentStaffRecord.sex || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="户籍住址:"> |
| | | {{ currentStaffRecord.nativePlace || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="岗位:"> |
| | | {{ currentStaffRecord.postName || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="现住址:"> |
| | | {{ currentStaffRecord.adress || '-' }} |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="离职日期:" prop="leaveDate"> |
| | | <el-date-picker |
| | | v-model="form.leaveDate" |
| | | <el-form-item label="离职日期:" |
| | | prop="leaveDate"> |
| | | <el-date-picker v-model="form.leaveDate" |
| | | type="date" |
| | | :disabled="operationType === 'edit'" |
| | | :disabled-date="disabledFutureDate" |
| | | placeholder="请选择离职日期" |
| | | value-format="YYYY-MM-DD" |
| | | format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | style="width: 100%" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="离职原因:" prop="reason"> |
| | | <el-select v-model="form.reason" placeholder="请选择离职原因" style="width: 100%" @change="handleSelectDimissionReason"> |
| | | <el-option |
| | | v-for="(item, index) in dimissionReasonOptions" |
| | | <el-form-item label="离职原因:" |
| | | prop="reason"> |
| | | <el-select v-model="form.reason" |
| | | placeholder="请选择离职原因" |
| | | style="width: 100%" |
| | | @change="handleSelectDimissionReason"> |
| | | <el-option v-for="(item, index) in dimissionReasonOptions" |
| | | :key="index" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row :gutter="30"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="备注:" prop="remark" v-if="form.reason === 'other'"> |
| | | <el-input |
| | | v-model="form.remark" |
| | | <el-form-item label="备注:" |
| | | prop="remark" |
| | | v-if="form.reason === 'other'"> |
| | | <el-input v-model="form.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="备注" |
| | | maxlength="500" |
| | | show-word-limit |
| | | /> |
| | | show-word-limit /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | |
| | | <!-- <el-row :gutter="30">--> |
| | | <!-- <el-col :span="12">--> |
| | | <!-- <div class="info-item">--> |
| | |
| | | <!-- </el-col>--> |
| | | <!-- </el-row>--> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">确认</el-button> |
| | | <el-button type="primary" |
| | | @click="submitForm">确认</el-button> |
| | | <el-button @click="closeDia">取消</el-button> |
| | | </div> |
| | | </template> |
| | |
| | | <script setup> |
| | | import {ref, reactive, toRefs, getCurrentInstance} from "vue"; |
| | | import {staffOnJobListPage} from "@/api/personnelManagement/staffOnJob.js"; |
| | | import {createStaffLeave, updateStaffLeave} from "@/api/personnelManagement/staffLeave.js"; |
| | | const { proxy } = getCurrentInstance() |
| | | const emit = defineEmits(['close']) |
| | | import { |
| | | createStaffLeave, |
| | | updateStaffLeave, |
| | | } from "@/api/personnelManagement/staffLeave.js"; |
| | | const { proxy } = getCurrentInstance(); |
| | | const emit = defineEmits(["close"]); |
| | | |
| | | const dialogFormVisible = ref(false); |
| | | const operationType = ref('') |
| | | const operationType = ref(""); |
| | | const getTodayDate = () => { |
| | | const now = new Date(); |
| | | const year = now.getFullYear(); |
| | | const month = `${now.getMonth() + 1}`.padStart(2, '0'); |
| | | const day = `${now.getDate()}`.padStart(2, '0'); |
| | | const month = `${now.getMonth() + 1}`.padStart(2, "0"); |
| | | const day = `${now.getDate()}`.padStart(2, "0"); |
| | | return `${year}-${month}-${day}`; |
| | | }; |
| | | |
| | | const disabledFutureDate = (time) => { |
| | | const disabledFutureDate = time => { |
| | | const todayEnd = new Date(); |
| | | todayEnd.setHours(23, 59, 59, 999); |
| | | return time.getTime() > todayEnd.getTime(); |
| | |
| | | }, |
| | | rules: { |
| | | staffName: [{ required: true, message: "请选择人员" }], |
| | | leaveDate: [{ required: true, message: "请选择离职日期", trigger: "change" }], |
| | | leaveDate: [ |
| | | { required: true, message: "请选择离职日期", trigger: "change" }, |
| | | ], |
| | | reason: [{ required: true, message: "请选择离职原因"}], |
| | | }, |
| | | dimissionReasonOptions: [ |
| | | {label: '薪资待遇', value: 'salary'}, |
| | | {label: '职业发展', value: 'career_development'}, |
| | | {label: '工作环境', value: 'work_environment'}, |
| | | {label: '个人原因', value: 'personal_reason'}, |
| | | {label: '其他', value: 'other'}, |
| | | { label: "薪资待遇", value: "salary" }, |
| | | { label: "职业发展", value: "career_development" }, |
| | | { label: "工作环境", value: "work_environment" }, |
| | | { label: "个人原因", value: "personal_reason" }, |
| | | { label: "其他", value: "other" }, |
| | | ], |
| | | currentStaffRecord: {}, |
| | | }); |
| | | const { form, rules, dimissionReasonOptions, currentStaffRecord } = toRefs(data); |
| | | const { form, rules, dimissionReasonOptions, currentStaffRecord } = |
| | | toRefs(data); |
| | | |
| | | // 打开弹框 |
| | | const openDialog = (type, row) => { |
| | | operationType.value = type; |
| | | dialogFormVisible.value = true; |
| | | if (operationType.value === 'edit') { |
| | | currentStaffRecord.value = row |
| | | form.value.staffOnJobId = row.staffOnJobId |
| | | form.value.leaveDate = row.leaveDate |
| | | form.value.reason = row.reason |
| | | form.value.remark = row.remark |
| | | if (operationType.value === "edit") { |
| | | currentStaffRecord.value = row; |
| | | form.value.staffOnJobId = row.staffOnJobId; |
| | | form.value.leaveDate = row.leaveDate; |
| | | form.value.reason = row.reason; |
| | | form.value.remark = row.remark; |
| | | personList.value = [ |
| | | { |
| | | staffName: row.staffName, |
| | | id: row.staffOnJobId, |
| | | } |
| | | ] |
| | | }, |
| | | ]; |
| | | } else { |
| | | form.value.leaveDate = getTodayDate() |
| | | getList() |
| | | form.value.leaveDate = getTodayDate(); |
| | | getList(); |
| | | } |
| | | } |
| | | }; |
| | | |
| | | const handleSelectDimissionReason = (val) => { |
| | | if (val === 'other') { |
| | | form.value.remark = '' |
| | | const handleSelectDimissionReason = val => { |
| | | if (val === "other") { |
| | | form.value.remark = ""; |
| | | } |
| | | } |
| | | }; |
| | | // 提交产品表单 |
| | | const submitForm = () => { |
| | | form.value.staffState = 0 |
| | | if (form.value.reason !== 'other') { |
| | | form.value.remark = '' |
| | | form.value.staffState = 0; |
| | | if (form.value.reason !== "other") { |
| | | form.value.remark = ""; |
| | | } |
| | | proxy.$refs["formRef"].validate(valid => { |
| | | if (valid) { |
| | |
| | | createStaffLeave(form.value).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }) |
| | | }); |
| | | } else { |
| | | updateStaffLeave(currentStaffRecord.value.id, form.value).then(res => { |
| | | proxy.$modal.msgSuccess("提交成功"); |
| | | closeDia(); |
| | | }) |
| | | }); |
| | | } |
| | | } |
| | | }) |
| | | |
| | | } |
| | | }); |
| | | }; |
| | | // 关闭弹框 |
| | | const closeDia = () => { |
| | | // 表单已注释,手动重置表单数据 |
| | |
| | | remark: "", |
| | | }; |
| | | dialogFormVisible.value = false; |
| | | emit('close') |
| | | emit("close"); |
| | | }; |
| | | |
| | | const personList = ref([]); |
| | |
| | | staffOnJobListPage({ |
| | | current: -1, |
| | | size: -1, |
| | | staffState: 1 |
| | | staffState: 1, |
| | | }).then(res => { |
| | | personList.value = res.data.records || [] |
| | | }) |
| | | personList.value = res.data.records || []; |
| | | }); |
| | | }; |
| | | |
| | | const handleSelect = (val) => { |
| | | let obj = personList.value.find(item => item.id === val) |
| | | currentStaffRecord.value = {} |
| | | const handleSelect = val => { |
| | | let obj = personList.value.find(item => item.id === val); |
| | | currentStaffRecord.value = {}; |
| | | if (obj) { |
| | | // 保留离职日期和离职原因,只更新员工信息 |
| | | currentStaffRecord.value = obj |
| | | currentStaffRecord.value = obj; |
| | | } |
| | | } |
| | | }; |
| | | defineExpose({ |
| | | openDialog, |
| | | }); |
| | |
| | | <div class="search_form mb20"> |
| | | <div> |
| | | <span class="search_title">姓名:</span> |
| | | <el-input |
| | | v-model="searchForm.staffName" |
| | | <el-input v-model="searchForm.staffName" |
| | | style="width: 240px" |
| | | placeholder="请输入姓名搜索" |
| | | @change="handleQuery" |
| | | clearable |
| | | :prefix-icon="Search" |
| | | /> |
| | | <el-button type="primary" @click="handleQuery" style="margin-left: 10px" |
| | | >搜索</el-button |
| | | > |
| | | :prefix-icon="Search" /> |
| | | <el-button type="primary" |
| | | @click="handleQuery" |
| | | style="margin-left: 10px">搜索</el-button> |
| | | </div> |
| | | <div> |
| | | <el-button type="primary" @click="openForm('add')">新增离职</el-button> |
| | | <el-button type="primary" |
| | | @click="openForm('add')">新增离职</el-button> |
| | | <el-button @click="handleOut">导出</el-button> |
| | | <!-- <el-button type="danger" plain @click="handleDelete">删除</el-button> --> |
| | | </div> |
| | | </div> |
| | | <div class="table_list"> |
| | | <PIMTable |
| | | rowKey="id" |
| | | <PIMTable rowKey="id" |
| | | :column="tableColumn" |
| | | :tableData="tableData" |
| | | :page="page" |
| | |
| | | @selection-change="handleSelectionChange" |
| | | :tableLoading="tableLoading" |
| | | @pagination="pagination" |
| | | :total="page.total" |
| | | ></PIMTable> |
| | | :total="page.total"></PIMTable> |
| | | </div> |
| | | <form-dia ref="formDia" @close="handleQuery"></form-dia> |
| | | <form-dia ref="formDia" |
| | | @close="handleQuery"></form-dia> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import { Search } from "@element-plus/icons-vue"; |
| | | import {onMounted, ref} from "vue"; |
| | | import FormDia from "@/views/personnelManagement/dimission/components/formDia.vue"; |
| | | import {findStaffLeaveListPage, batchDeleteStaffLeaves} from "@/api/personnelManagement/staffLeave.js"; |
| | | import { |
| | | findStaffLeaveListPage, |
| | | batchDeleteStaffLeaves, |
| | | } from "@/api/personnelManagement/staffLeave.js"; |
| | | import {ElMessageBox} from "element-plus"; |
| | | |
| | | const data = reactive({ |
| | |
| | | label: "状态", |
| | | prop: "staffState", |
| | | dataType: "tag", |
| | | formatData: (params) => { |
| | | formatData: params => { |
| | | if (params == 0) { |
| | | return "离职"; |
| | | } else if (params == 1) { |
| | |
| | | return null; |
| | | } |
| | | }, |
| | | formatType: (params) => { |
| | | formatType: params => { |
| | | if (params == 0) { |
| | | return "danger"; |
| | | } else if (params == 1) { |
| | |
| | | prop: "sex", |
| | | }, |
| | | { |
| | | label: "户籍住址", |
| | | prop: "nativePlace", |
| | | }, |
| | | { |
| | | label: "部门", |
| | | prop: "deptName", |
| | | }, |
| | |
| | | prop: "postName", |
| | | }, |
| | | { |
| | | label: "现住址", |
| | | prop: "adress", |
| | | width:200 |
| | | }, |
| | | { |
| | | label: "第一学历", |
| | | prop: "firstStudy", |
| | | }, |
| | | { |
| | | label: "专业", |
| | | prop: "profession", |
| | | width:100 |
| | | width: 100, |
| | | }, |
| | | { |
| | | label: "年龄", |
| | |
| | | { |
| | | label: "联系电话", |
| | | prop: "phone", |
| | | width:150 |
| | | width: 150, |
| | | }, |
| | | { |
| | | label: "紧急联系人", |
| | | prop: "emergencyContact", |
| | | width: 120 |
| | | width: 120, |
| | | }, |
| | | { |
| | | label: "紧急联系人电话", |
| | | prop: "emergencyContactPhone", |
| | | width:150 |
| | | width: 150, |
| | | }, |
| | | { |
| | | dataType: "action", |
| | | label: "操作", |
| | | align: "center", |
| | | fixed: 'right', |
| | | fixed: "right", |
| | | operation: [ |
| | | { |
| | | name: "编辑", |
| | | type: "text", |
| | | clickFun: (row) => { |
| | | clickFun: row => { |
| | | openForm("edit", row); |
| | | }, |
| | | }, |
| | |
| | | size: 100, |
| | | total: 0, |
| | | }); |
| | | const formDia = ref() |
| | | const { proxy } = getCurrentInstance() |
| | | |
| | | const formDia = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | |
| | | // 查询列表 |
| | | /** 搜索按钮操作 */ |
| | |
| | | page.current = 1; |
| | | getList(); |
| | | }; |
| | | const pagination = (obj) => { |
| | | const pagination = obj => { |
| | | page.current = obj.page; |
| | | page.size = obj.limit; |
| | | getList(); |
| | | }; |
| | | const getList = () => { |
| | | tableLoading.value = true; |
| | | findStaffLeaveListPage({...page, ...searchForm.value}).then(res => { |
| | | findStaffLeaveListPage({ ...page, ...searchForm.value }) |
| | | .then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records |
| | | tableData.value = res.data.records; |
| | | page.total = res.data.total; |
| | | }).catch(err => { |
| | | tableLoading.value = false; |
| | | }) |
| | | .catch(err => { |
| | | tableLoading.value = false; |
| | | }); |
| | | }; |
| | | // 表格选择数据 |
| | | const handleSelectionChange = (selection) => { |
| | | const handleSelectionChange = selection => { |
| | | selectedRows.value = selection; |
| | | }; |
| | | |
| | | // 打开弹框 |
| | | const openForm = (type, row) => { |
| | | nextTick(() => { |
| | | formDia.value?.openDialog(type, row) |
| | | }) |
| | | formDia.value?.openDialog(type, row); |
| | | }); |
| | | }; |
| | | |
| | | // 删除 |
| | | const handleDelete = () => { |
| | | let ids = []; |
| | | if (selectedRows.value.length > 0) { |
| | | ids = selectedRows.value.map((item) => item.id); |
| | | ids = selectedRows.value.map(item => item.id); |
| | | } else { |
| | | proxy.$modal.msgWarning("请选择数据"); |
| | | return; |
| | |
| | | type: "warning", |
| | | }) |
| | | .then(() => { |
| | | batchDeleteStaffLeaves(ids).then((res) => { |
| | | batchDeleteStaffLeaves(ids).then(res => { |
| | | proxy.$modal.msgSuccess("删除成功"); |
| | | getList(); |
| | | }); |
| | |
| | | prop="paymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="退货金额(元)" |
| | | prop="returnAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" /> |
| | | <el-table-column label="应付金额(元)" |
| | | prop="payableAmount" |
| | | show-overflow-tooltip> |
| | |
| | | }, |
| | | }, |
| | | { |
| | | label: "退货金额(元)", |
| | | prop: "returnAmount", |
| | | width: 200, |
| | | formatData: params => { |
| | | return params ? parseFloat(params).toFixed(2) : 0; |
| | | }, |
| | | }, |
| | | { |
| | | label: "应付金额(元)", |
| | | dataType: "slot", |
| | | width: 200, |
| | |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["contractAmounts", "paymentAmount", "payableAmount"], |
| | | ["contractAmounts", "paymentAmount", "returnAmount", "payableAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // 不保留小数 |
| | | futureTickets: { noDecimal: true }, // 不保留小数 |
| | |
| | | const summarizeMainTable1 = param => { |
| | | let summarizeTable = proxy.summarizeTable( |
| | | param, |
| | | ["contractAmount", "invoiceAmount", "paymentAmount"], |
| | | ["contractAmount", "invoiceAmount", "paymentAmount", "returnAmount"], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // 不保留小数 |
| | | futureTickets: { noDecimal: true }, // 不保留小数 |
| | |
| | | </el-table-column> |
| | | <el-table-column label="数量" |
| | | prop="quantity" /> |
| | | <el-table-column label="销售发货数量" |
| | | prop="shippedQuantity" /> |
| | | <el-table-column label="可用数量" |
| | | prop="availableQuality" /> |
| | | <el-table-column label="退货数量" |
| | |
| | | </el-form-item> |
| | | |
| | | <el-form-item |
| | | label="发货类型" |
| | | prop="shippingType" |
| | | :rules="[ |
| | | { |
| | | required: true, |
| | | message: '请选择发货类型', |
| | | trigger: 'change', |
| | | } |
| | | ]" |
| | | > |
| | | <el-select |
| | | v-model="formState.shippingType" |
| | | placeholder="请选择发货类型" |
| | | style="width: 240px" |
| | | @change="handleShippingTypeChange" |
| | | > |
| | | <el-option label="货车" :value="1" /> |
| | | <el-option label="快递" :value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | |
| | | <el-form-item |
| | | v-if="formState.shippingType === 1" |
| | | label="发货车牌号" |
| | | prop="truckPlateNo" |
| | | > |
| | | <el-input |
| | | v-model="formState.truckPlateNo" |
| | | placeholder="请输入发货车牌号" |
| | | style="width: 240px" |
| | | /> |
| | | </el-form-item> |
| | | |
| | | <template v-else-if="formState.shippingType === 2"> |
| | | <el-form-item |
| | | label="快递公司" |
| | | prop="expressCompany" |
| | | > |
| | | <el-input |
| | | v-model="formState.expressCompany" |
| | | placeholder="请输入快递公司" |
| | | style="width: 240px" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item |
| | | label="快递单号" |
| | | prop="expressNo" |
| | | > |
| | | <el-input |
| | | v-model="formState.expressNo" |
| | | placeholder="请输入快递单号" |
| | | style="width: 240px" |
| | | /> |
| | | </el-form-item> |
| | | </template> |
| | | |
| | | <el-form-item |
| | | label="供应商名称" |
| | | prop="supplierId" |
| | | :rules="[ |
| | |
| | | <el-table-column label="数量" |
| | | prop="stockInNum" |
| | | width="100" /> |
| | | <el-table-column label="销售发货数量" |
| | | prop="saleOutQuantity" |
| | | width="120" /> |
| | | <el-table-column label="可退货数量" |
| | | prop="unQuantity" |
| | | width="130" /> |
| | | <el-table-column label="已退货数量" |
| | | width="130"> |
| | | <template #default="scope"> |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | {{ formattedNumber(scope.row, null, scope.row.totalReturnNum || 0) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="退货数量" |
| | |
| | | no: '', |
| | | isDefaultNo: true, |
| | | returnType: 0, |
| | | shippingType: undefined, |
| | | truckPlateNo: '', |
| | | expressCompany: '', |
| | | expressNo: '', |
| | | incomeType: undefined, |
| | | remark: '', |
| | | supplierId: undefined, |
| | |
| | | // 是否展示产品列表数据 |
| | | const isShowProductsModal = ref(false) |
| | | |
| | | const handleShippingTypeChange = (val) => { |
| | | if (val === 1) { |
| | | formState.value.expressCompany = ''; |
| | | formState.value.expressNo = ''; |
| | | } else if (val === 2) { |
| | | formState.value.truckPlateNo = ''; |
| | | } else { |
| | | formState.value.truckPlateNo = ''; |
| | | formState.value.expressCompany = ''; |
| | | formState.value.expressNo = ''; |
| | | } |
| | | }; |
| | | |
| | | const isShow = computed({ |
| | | get() { |
| | | return props.visible; |
| | |
| | | const toNumber = (val) => { |
| | | const num = Number(val) |
| | | return Number.isNaN(num) ? 0 : num |
| | | } |
| | | |
| | | /** 已退货数量 = 入库行总数量 − 当前可退货数量(剩余) */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0) |
| | | const un = Number(row?.unQuantity ?? 0) |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0 |
| | | return Math.max(total - un, 0) |
| | | } |
| | | |
| | | const getReturnTotal = (row) => { |
| | |
| | | } |
| | | |
| | | const closeModal = () => { |
| | | formState.value.shippingType = undefined; |
| | | formState.value.truckPlateNo = ''; |
| | | formState.value.expressCompany = ''; |
| | | formState.value.expressNo = ''; |
| | | isShow.value = false; |
| | | }; |
| | | |
| | |
| | | <el-table-column label="数量" |
| | | prop="stockInNum" |
| | | width="70" /> |
| | | <el-table-column label="销售发货数量" |
| | | prop="saleOutQuantity" |
| | | width="120" /> |
| | | <el-table-column label="可退货数量" |
| | | prop="unQuantity" |
| | | width="130" /> |
| | | <el-table-column label="已退货数量" |
| | | width="130"> |
| | | <template #default="scope"> |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | {{ formattedNumber(scope.row, null, scope.row.totalReturnNum || 0) }} |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="库存预警数量" |
| | |
| | | const formattedNumber = (row, column, cellValue) => { |
| | | return parseFloat(cellValue).toFixed(2); |
| | | }; |
| | | |
| | | /** 已退货数量 = 入库行总数量 − 当前可退货数量(剩余) */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0) |
| | | const un = Number(row?.unQuantity ?? 0) |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0 |
| | | return Math.max(total - un, 0) |
| | | } |
| | | |
| | | const handleChangeSelection = (val) => { |
| | | selectedRows.value = val; |
| | |
| | | @click="handleDetail(row)" |
| | | >详情</el-button |
| | | > |
| | | <el-button link size="small" @click="handleDelete(row)" |
| | | >删除</el-button |
| | | <el-button |
| | | link |
| | | size="small" |
| | | :disabled="row.stockOutApproved" |
| | | :style="{ color: row.stockOutApproved ? '#c0c4cc' : undefined }" |
| | | @click="handleDelete(row)" |
| | | >{{ row.stockOutApproved }}</el-button |
| | | > |
| | | </template> |
| | | </PIMTable> |
| | |
| | | <el-descriptions-item label="退料人">{{ |
| | | detailData.returnUserName || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="发货类型">{{ |
| | | getShippingTypeLabel(detailData.shippingType) |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item v-if="String(detailData.shippingType) === '1'" label="发货车牌号">{{ |
| | | detailData.truckPlateNo || "--" |
| | | }}</el-descriptions-item> |
| | | <template v-else-if="String(detailData.shippingType) === '2'"> |
| | | <el-descriptions-item label="快递公司">{{ |
| | | detailData.expressCompany || "--" |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="快递单号">{{ |
| | | detailData.expressNo || "--" |
| | | }}</el-descriptions-item> |
| | | </template> |
| | | <el-descriptions-item label="整单折扣额">{{ |
| | | formatAmount(detailData.totalDiscountAmount) |
| | | }}</el-descriptions-item> |
| | |
| | | /> |
| | | <el-table-column label="单位" prop="unit" width="80" /> |
| | | <el-table-column label="数量" prop="stockInNum" width="80" /> |
| | | <el-table-column label="销售发货数量" prop="saleOutQuantity" width="110" /> |
| | | <el-table-column label="可退货数量" |
| | | prop="unQuantity" |
| | | width="100" /> |
| | | <el-table-column label="已退货数量" |
| | | width="100"> |
| | | <template #default="scope"> |
| | | {{ calcAlreadyReturned(scope.row) }} |
| | | {{ formatAmount(scope.row.totalReturnNum || 0) }} |
| | | </template> |
| | | </el-table-column> |
| | | <!-- <el-table-column label="库存预警数量" prop="warnNum" width="120" /> --> |
| | |
| | | prop: "returnUserName", |
| | | width: 110, |
| | | }, |
| | | { |
| | | label: "发货类型", |
| | | prop: "shippingType", |
| | | width: 100, |
| | | formatData: (val) => getShippingTypeLabel(val), |
| | | }, |
| | | { label: "发货车牌号", prop: "truckPlateDisplay", width: 140 }, |
| | | { label: "快递公司", prop: "expressCompanyDisplay", width: 140 }, |
| | | { label: "快递单号", prop: "expressNoDisplay", width: 150 }, |
| | | |
| | | { |
| | | label: "整单折扣额", |
| | |
| | | }, |
| | | { |
| | | name: "删除", |
| | | disabled: (row) => !!row.stockOutApproved, |
| | | clickFun: (row) => { |
| | | handleDelete(row); |
| | | }, |
| | |
| | | findPurchaseReturnOrderListPage({ ...searchForm.value, ...page }) |
| | | .then((res) => { |
| | | tableLoading.value = false; |
| | | tableData.value = res.data.records; |
| | | tableData.value = (res.data.records || []).map((row) => ({ |
| | | ...row, |
| | | truckPlateDisplay: String(row?.shippingType) === "1" ? (row?.truckPlateNo || "--") : "--", |
| | | expressCompanyDisplay: String(row?.shippingType) === "2" ? (row?.expressCompany || "--") : "--", |
| | | expressNoDisplay: String(row?.shippingType) === "2" ? (row?.expressNo || "--") : "--", |
| | | })); |
| | | page.total = res.data.total; |
| | | }) |
| | | .catch(() => { |
| | |
| | | ); |
| | | }; |
| | | |
| | | const getShippingTypeLabel = (value) => { |
| | | const shippingTypeMap = { |
| | | 1: "货车", |
| | | 2: "快递", |
| | | }; |
| | | return shippingTypeMap[String(value)] || shippingTypeMap[value] || "--"; |
| | | }; |
| | | |
| | | const formatAmount = (value) => { |
| | | if (value === null || value === undefined || value === "") { |
| | | return "--"; |
| | |
| | | return num.toFixed(2); |
| | | }; |
| | | |
| | | /** 已退货数量 = 入库行总数量 − 当前可退货数量(剩余) */ |
| | | const calcAlreadyReturned = (row) => { |
| | | const total = Number(row?.stockInNum ?? row?.totalQuantity ?? row?.quantity ?? 0); |
| | | const un = Number(row?.unQuantity ?? 0); |
| | | if (!Number.isFinite(total) || !Number.isFinite(un)) return 0; |
| | | return Math.max(total - un, 0); |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | }); |
| | |
| | | margin-top: unset; |
| | | } |
| | | </style> |
| | | |
| | |
| | | <div class="app-container indicator-stats"> |
| | | <!-- KPI 汇总 --> |
| | | <el-row :gutter="20" class="stats-row"> |
| | | <el-col :xs="24" :sm="12" :md="8"> |
| | | <el-col :xs="24" :sm="12" :md="12"> |
| | | <div class="stat-card stat-card-blue"> |
| | | <div class="stat-icon-wrapper"> |
| | | <div class="stat-icon"> |
| | |
| | | <div class="stat-bg-decoration"></div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8"> |
| | | <el-col :xs="24" :sm="12" :md="12"> |
| | | <div class="stat-card stat-card-green"> |
| | | <div class="stat-icon-wrapper"> |
| | | <div class="stat-icon"> |
| | |
| | | <div class="stat-content"> |
| | | <div class="stat-value">¥{{ indicatorKpis.salesAmount.toLocaleString() }}</div> |
| | | <div class="stat-label">销售额</div> |
| | | </div> |
| | | <div class="stat-bg-decoration"></div> |
| | | </div> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="8"> |
| | | <div class="stat-card stat-card-orange"> |
| | | <div class="stat-icon-wrapper"> |
| | | <div class="stat-icon"> |
| | | <el-icon :size="32"><Van /></el-icon> |
| | | </div> |
| | | </div> |
| | | <div class="stat-content"> |
| | | <div class="stat-value">{{ indicatorKpis.shipRate }}</div> |
| | | <div class="stat-label">发货率</div> |
| | | </div> |
| | | <div class="stat-bg-decoration"></div> |
| | | </div> |
| | |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue' |
| | | import { Document, Van, Tickets, Search, Refresh } from '@element-plus/icons-vue' |
| | | import { Document, Tickets, Search, Refresh } from '@element-plus/icons-vue' |
| | | import * as echarts from 'echarts' |
| | | import { getTotalStatistics, getStatisticsTable } from '@/api/salesManagement/indicatorStats' |
| | | import { productTreeList } from '@/api/basicData/product.js' |
| | |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" /> |
| | | <el-table-column label="退货金额(元)" |
| | | prop="returnAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" /> |
| | | <el-table-column label="应收金额(元)" |
| | | prop="receiptableAmount" |
| | | show-overflow-tooltip |
| | |
| | | width="200" /> |
| | | <el-table-column label="回款金额(元)" |
| | | prop="receiptPaymentAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" /> |
| | | <el-table-column label="退货金额(元)" |
| | | prop="returnAmount" |
| | | show-overflow-tooltip |
| | | :formatter="formattedNumber" |
| | | width="200" /> |
| | |
| | | const summarizeMainTable = param => { |
| | | return proxy.summarizeTable( |
| | | param, |
| | | ["invoiceTotal", "receiptPaymentAmount", "unReceiptPaymentAmount"], |
| | | [ |
| | | "contractAmounts", |
| | | "receiptPaymentAmount", |
| | | "returnAmount", |
| | | "receiptableAmount", |
| | | ], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // 不保留小数 |
| | | futureTickets: { noDecimal: true }, // 不保留小数 |
| | |
| | | const summarizeMainTable1 = param => { |
| | | var summarizeTable = proxy.summarizeTable( |
| | | param, |
| | | ["contractAmount", "receiptPaymentAmount", "receiptableAmount"], |
| | | [ |
| | | "contractAmount", |
| | | "receiptPaymentAmount", |
| | | "returnAmount", |
| | | "receiptableAmount", |
| | | ], |
| | | { |
| | | ticketsNum: { noDecimal: true }, // 不保留小数 |
| | | futureTickets: { noDecimal: true }, // 不保留小数 |
| | |
| | | <el-descriptions-item label="销售单号">{{ detail.salesContractNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="业务员">{{ detail.salesman }}</el-descriptions-item> |
| | | <el-descriptions-item label="关联发货单号">{{ detail.shippingNo }}</el-descriptions-item> |
| | | <el-descriptions-item label="发货类型">{{ getShippingTypeText(detail.shippingType) }}</el-descriptions-item> |
| | | <el-descriptions-item v-if="String(detail.shippingType) === '1'" label="发货车牌号">{{ detail.truckPlateNo || "--" }}</el-descriptions-item> |
| | | <template v-else-if="String(detail.shippingType) === '2'"> |
| | | <el-descriptions-item label="快递公司">{{ detail.expressCompany || "--" }}</el-descriptions-item> |
| | | <el-descriptions-item label="快递单号">{{ detail.expressNo || "--" }}</el-descriptions-item> |
| | | </template> |
| | | <!-- <el-descriptions-item label="项目名称">{{ detail.projectName }}</el-descriptions-item> --> |
| | | <el-descriptions-item label="制单人">{{ detail.maker }}</el-descriptions-item> |
| | | <el-descriptions-item label="制单时间">{{ detail.makeTime }}</el-descriptions-item> |
| | |
| | | const detail = ref({}); |
| | | const tableData = ref([]); |
| | | const availableProducts = ref([]); |
| | | const shippingTypeMap = { |
| | | 1: "货车", |
| | | 2: "快递", |
| | | }; |
| | | |
| | | const sameKey = (a, b) => a != null && b != null && String(a) === String(b); |
| | | |
| | |
| | | return Math.max(total - un, 0); |
| | | }; |
| | | |
| | | const getShippingTypeText = (value) => shippingTypeMap[String(value)] || shippingTypeMap[value] || "--"; |
| | | |
| | | /** 详情表用 productName / model;合并时勿让空串盖掉出库行字段 */ |
| | | const mergeDetailProductRow = (product, normalized) => { |
| | | const row = { ...product, ...normalized }; |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="发货类型:" prop="shippingType"> |
| | | <el-select v-model="form.shippingType" placeholder="请选择发货类型" @change="handleShippingTypeChange"> |
| | | <el-option label="货车" :value="1" /> |
| | | <el-option label="快递" :value="2" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4" v-if="form.shippingType === 1"> |
| | | <el-form-item label="发货车牌号:" prop="truckPlateNo"> |
| | | <el-input v-model="form.truckPlateNo" placeholder="请输入发货车牌号" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <template v-else-if="form.shippingType === 2"> |
| | | <el-col :span="4"> |
| | | <el-form-item label="快递公司:" prop="expressCompany"> |
| | | <el-input v-model="form.expressCompany" placeholder="请输入快递公司" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="4"> |
| | | <el-form-item label="快递单号:" prop="expressNo"> |
| | | <el-input v-model="form.expressNo" placeholder="请输入快递单号" /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </template> |
| | | <el-col :span="4"> |
| | | <el-form-item label="退款总额:" prop="refundAmount"> |
| | | <el-input v-model="form.refundAmount" disabled placeholder="自动计算" /> |
| | | </el-form-item> |
| | |
| | | returnNo: "", |
| | | customerId: "", |
| | | shippingId: "", |
| | | shippingType: "", |
| | | truckPlateNo: "", |
| | | expressCompany: "", |
| | | expressNo: "", |
| | | projectId: "", |
| | | maker: "", |
| | | makeTime: "", |
| | |
| | | }], |
| | | customerId: [{ required: true, message: "请选择客户", trigger: "change" }], |
| | | shippingId: [{ required: true, message: "请选择关联出库单号", trigger: "change" }], |
| | | shippingType: [{ required: true, message: "请选择发货类型", trigger: "change" }], |
| | | } |
| | | }); |
| | | const { form, rules } = toRefs(data); |
| | |
| | | returnNo: "", |
| | | customerId: "", |
| | | shippingId: "", |
| | | shippingType: "", |
| | | truckPlateNo: "", |
| | | expressCompany: "", |
| | | expressNo: "", |
| | | projectId: "", |
| | | maker: "", |
| | | makeTime: "", |
| | |
| | | const payload = { ...form.value, returnSaleProducts }; |
| | | delete payload.returnNoCheckbox; |
| | | if (operationType.value === "add" && form.value.returnNoCheckbox) delete payload.returnNo; |
| | | if (payload.shippingType !== 1) payload.truckPlateNo = ""; |
| | | if (payload.shippingType !== 2) { |
| | | payload.expressCompany = ""; |
| | | payload.expressNo = ""; |
| | | } |
| | | if (operationType.value === "add") { |
| | | returnManagementAdd(payload).then(() => { |
| | | proxy.$modal.msgSuccess("新增成功"); |
| | |
| | | returnNo: "", |
| | | customerId: "", |
| | | shippingId: "", |
| | | shippingType: "", |
| | | truckPlateNo: "", |
| | | expressCompany: "", |
| | | expressNo: "", |
| | | projectId: "", |
| | | maker: "", |
| | | makeTime: "", |
| | |
| | | formRef.value?.validateField('returnNo'); |
| | | }; |
| | | |
| | | const handleShippingTypeChange = (val) => { |
| | | if (val === 1) { |
| | | form.value.expressCompany = ""; |
| | | form.value.expressNo = ""; |
| | | } else if (val === 2) { |
| | | form.value.truckPlateNo = ""; |
| | | } else { |
| | | form.value.truckPlateNo = ""; |
| | | form.value.expressCompany = ""; |
| | | form.value.expressNo = ""; |
| | | } |
| | | }; |
| | | |
| | | const customerNameChange = async (val, clearDownstream = true) => { |
| | | // val is customerId now |
| | | if (clearDownstream) { |
| | |
| | | { label: "销售单号", prop: "salesContractNo", minWidth: 160 }, |
| | | { label: "业务员", prop: "salesman", minWidth: 120 }, |
| | | { label: "关联发货单号", prop: "shippingNo", minWidth: 170 }, |
| | | { |
| | | label: "发货类型", |
| | | prop: "shippingType", |
| | | minWidth: 100, |
| | | formatData: (val) => ({ 1: "货车", 2: "快递" }[String(val)] || "--"), |
| | | }, |
| | | { label: "发货车牌号", prop: "truckPlateDisplay", minWidth: 140 }, |
| | | { label: "快递公司", prop: "expressCompanyDisplay", minWidth: 140 }, |
| | | { label: "快递单号", prop: "expressNoDisplay", minWidth: 150 }, |
| | | { label: "项目名称", prop: "projectName", minWidth: 180 }, |
| | | { label: "制单人", prop: "maker", minWidth: 120 }, |
| | | { |
| | |
| | | tableLoading.value = true; |
| | | returnManagementList({ ...searchForm.value, ...page }).then(res => { |
| | | tableLoading.value = false; |
| | | tableData.value = res?.data?.records || []; |
| | | tableData.value = (res?.data?.records || []).map((row) => ({ |
| | | ...row, |
| | | truckPlateDisplay: String(row?.shippingType) === "1" ? (row?.truckPlateNo || "--") : "--", |
| | | expressCompanyDisplay: String(row?.shippingType) === "2" ? (row?.expressCompany || "--") : "--", |
| | | expressNoDisplay: String(row?.shippingType) === "2" ? (row?.expressNo || "--") : "--", |
| | | })); |
| | | page.total = res?.data?.total || 0; |
| | | }).finally(() => tableLoading.value = false); |
| | | }; |