ME 4 dni temu
rodzic
commit
f15f585ec8

+ 43 - 111
src/views/emergency/schedule-add-or-update.vue

@@ -1,39 +1,10 @@
 <template>
   <el-dialog v-model="visible" :title="!dataForm.id ? '新增' : '修改'" :close-on-click-modal="false" :close-on-press-escape="false">
     <el-form :model="dataForm" :rules="rules" ref="dataFormRef" @keyup.enter="dataFormSubmitHandle()" label-width="120px">
-          <el-form-item label="排班表名称" prop="scheduleName">
-        <el-input v-model="dataForm.scheduleName" placeholder="排班表名称"></el-input>
-      </el-form-item>
-          <el-form-item label="排班开始日期" prop="periodStart">
-            <el-date-picker
-            v-model="dataForm.periodStart"
-            type="date"        
-            placeholder="选择开始日期"
-            value-format="YYYY-MM-DD" 
-            style="width: 100%;"
-          ></el-date-picker>
-      </el-form-item>
-          <el-form-item label="排班结束日期" prop="periodEnd">
-            <el-date-picker
-            v-model="dataForm.periodEnd"
-            type="date"        
-            placeholder="选择结束日期"
-            value-format="YYYY-MM-DD" 
-            style="width: 100%;"
-          ></el-date-picker>
-      </el-form-item>
-      <el-form-item label="上传文件" prop="filePath">
-            <el-upload
-              class="avatar-uploader"
-              :action="action"
-              :headers="headers"
-              :show-file-list="false"
-              :on-success="handleAvatarSuccess"
-              :before-upload="beforeAvatarUpload"
-            >
-              <img v-if="imageUrl" :src="imageUrl" class="avatar" />
-              <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
-            </el-upload>
+      <el-form-item label="员工姓名" prop="employeeIds">
+            <el-select v-model="dataForm.employeeIds" placeholder="员工姓名" multiple collapse-tags collapse-tags-tooltip>
+              <el-option v-for="item in employeeOptions" :key="item.id" :label="item.name" :value="item.id" ></el-option>
+          </el-select>
       </el-form-item>
           <el-form-item label="是否确认" prop="status">
             <el-select v-model="dataForm.status" placeholder="是否确认">
@@ -52,95 +23,85 @@
 </template>
 
 <script lang="ts" setup>
-import app from "@/constants/app";
-import { getToken } from "@/utils/cache";
+
 import { reactive, ref } from "vue";
 import baseService from "@/service/baseService";
 import { ElMessage } from "element-plus";
-import { Plus } from '@element-plus/icons-vue'
-import { parseTime } from '@/utils/ruoyi'
-import type { UploadProps } from 'element-plus'
+
 const emit = defineEmits(["refreshDataList"]);
 import useView from "@/hooks/useView";
 const state = reactive({ ...useView({}) });
-const action = `${app.api}/sys/oss/upload`;
-const headers = {token:  getToken() }
-const imageUrl = ref('');
+
 const visible = ref(false);
 const dataFormRef = ref();
 
 const dataForm = reactive({
-  id: '',  scheduleName: '',  periodStart: '',  periodEnd: '',  filePath: '',  status: '',  creator: '',  createDate: '',  updater: '',  updateDate: '',  remark: ''});
+  id: '',yearMonth: '', scheduleDate:'', employeeIds: [] as string[], employeeNames:'', employeeId: 0, employeeName: '', filePath: '',  status: '',  creator: '',  createDate: '',  updater: '',  updateDate: '',  remark: ''});
 
 const rules = ref({
-          scheduleName: [
-      { required: true, message: '必填项不能为空', trigger: 'blur' }
-    ],
-          periodStart: [
-      { required: true, message: '必填项不能为空', trigger: 'blur' }
-    ],
-          periodEnd: [
-      { required: true, message: '必填项不能为空', trigger: 'blur' }
-    ],
+          employeeIds: [
+    { required: true, type: 'array', min: 1, message: '请选择至少一名员工', trigger: 'change' }
+  ],
           status: [
       { required: true, message: '必填项不能为空', trigger: 'blur' }
     ]
   });
 
-const init = (id?: number) => {
+const props = defineProps<{ defaultDate?: string }>();
+
+const multipleDates = ref<string[]>([]);
+const init = (id?: number, date?: string, dates?: string[]) => {
   visible.value = true;
   dataForm.id = "";
-
-  // 重置表单数据
+  dataForm.scheduleDate = date || '';
+  
   if (dataFormRef.value) {
     dataFormRef.value.resetFields();
-    imageUrl.value = '';
   }
-
+  fetchEmployeeOptions();
+  multipleDates.value = dates || [];
   if (id) {
     getInfo(id);
   }
 };
 
-const handleAvatarSuccess: UploadProps['onSuccess'] = (
-  response,
-  uploadFile
-) => {
-  if(response.code!==0) return ElMessage.error(response.msg);
-  imageUrl.value = response?.data?.src;
-}
-
-const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
-  if (!['image/jpeg','image/png','image/jpp'].includes(rawFile.type)) {
-    ElMessage.error('请上传jpg/png格式的图片!')
-    return false
-  } else if (rawFile.size / 1024 / 1024 > 10) {
-    ElMessage.error('图片大小不能超过10MB!')
-    return false
-  }
-  return true
-}
+const employeeOptions = ref<{ id: string; name: string }[]>([]);
+
+// 获取员工数据
+const fetchEmployeeOptions = async () => {
+  const res = await baseService.get("/emergency/employee/page");
+  employeeOptions.value = res.data.list || [];
+};
 
 // 获取信息
 const getInfo = (id: number) => {
   baseService.get("/emergency/schedule/" + id).then((res) => {
     Object.assign(dataForm, res.data);
     dataForm.status = dataForm.status + '';
-    imageUrl.value = dataForm.filePath;
   });
 };
 
-// 表单提交
 const dataFormSubmitHandle = () => {
   dataFormRef.value.validate((valid: boolean) => {
     if (!valid) {
       return false;
     }
-    if(!imageUrl.value) return ElMessage.error('请上传图片');
-    dataForm.periodStart = dataForm.periodStart ? parseTime(new Date(dataForm.periodStart), '{yy}-{mm}-{dd}') : null;
-    dataForm.periodEnd = dataForm.periodEnd ? parseTime(new Date(dataForm.periodEnd), '{yy}-{mm}-{dd} ') : null;
-    dataForm.filePath = imageUrl.value;
-    (!dataForm.id ? baseService.post : baseService.put)("/emergency/schedule", dataForm).then((res) => {
+
+    const baseDate = dataForm.scheduleDate || multipleDates.value[0] || '';
+    const yearMonth = baseDate.slice(0, 7);
+    const dates = multipleDates.value.length > 0 ? multipleDates.value : [dataForm.scheduleDate];
+    const scheduleDTOs = dates.map(date => ({
+      scheduleDate: date,
+      employeeIds: dataForm.employeeIds,
+    }));
+
+    const payload = {
+      yearMonth,
+      scheduleDTOs,
+      remark: dataForm.remark
+    };
+
+    baseService.post("/emergency/schedule/batch", payload).then((res) => {
       ElMessage.success({
         message: '成功',
         duration: 500,
@@ -153,37 +114,8 @@ const dataFormSubmitHandle = () => {
   });
 };
 
+
 defineExpose({
   init
 });
 </script>
-<style scoped>
-.avatar-uploader .avatar {
-  width: 178px;
-  height: 178px;
-  display: block;
-}
-</style>
-
-<style>
-.avatar-uploader .el-upload {
-  border: 1px dashed var(--el-border-color);
-  border-radius: 6px;
-  cursor: pointer;
-  position: relative;
-  overflow: hidden;
-  transition: var(--el-transition-duration-fast);
-}
-
-.avatar-uploader .el-upload:hover {
-  border-color: var(--el-color-primary);
-}
-
-.el-icon.avatar-uploader-icon {
-  font-size: 28px;
-  color: #8c939d;
-  width: 178px;
-  height: 178px;
-  text-align: center;
-}
-</style>

+ 128 - 55
src/views/emergency/schedule.vue

@@ -2,27 +2,6 @@
   <div class="mod-demo__schedule">
     <div class="form-container">
     <el-form :inline="true" :model="state.dataForm" @keyup.enter="state.getDataList()">
-      <el-form-item>
-        <el-input v-model="state.dataForm.schedule_name" placeholder="排班表名称" clearable></el-input>
-      </el-form-item>
-      <el-form-item >
-        <el-date-picker
-          v-model="state.dataForm.period_start"
-          type="date"        
-          placeholder="开始日期"
-          value-format="YYYY-MM-DD" 
-          clearable
-        ></el-date-picker>
-      </el-form-item>
-      <el-form-item >
-        <el-date-picker
-          v-model="state.dataForm.period_end"
-          type="date"        
-          placeholder="结束日期"
-          value-format="YYYY-MM-DD" 
-          clearable
-        ></el-date-picker>
-      </el-form-item>
       <el-form-item>
         <el-select v-model="state.dataForm.status" placeholder="排班确认状态"  clearable>
           <el-option :label="item.dictLabel" :value="item.dictValue" v-for="item in state.getDictByKey('scheduleStatus')"></el-option>
@@ -32,8 +11,7 @@
         <el-button @click="state.getDataList()">查询</el-button>
       </el-form-item>
       <el-form-item>
-      <el-form-item>
-        <el-button v-if="state.hasPermission('emergency:schedule:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
+        <el-button v-if="state.hasPermission('emergency:schedule:save')" type="primary" @click="addHandle()">新增</el-button>
       </el-form-item>
       <el-form-item>
         <el-button v-if="state.hasPermission('emergency:schedule:delete')" type="danger" @click="state.deleteHandle()">删除</el-button>
@@ -44,40 +22,33 @@
       <el-form-item>
         <el-button v-if="state.hasPermission('emergency:inspection:export')" type="info" @click="state.exportHandle()">导出</el-button>
       </el-form-item>
-    </el-form-item>
     </el-form>
   </div>
-    <el-table v-loading="state.dataListLoading" :data="state.dataList" border @selection-change="state.dataListSelectionChangeHandle" style="width: 100%">
-      <el-table-column type="selection" header-align="center" align="center" width="50"></el-table-column>
-              <!-- <el-table-column prop="id" label="排班ID" header-align="center" align="center"></el-table-column> -->
-              <el-table-column prop="id" label="序号" header-align="center" align="center" width="70">
-                <template v-slot="scope">{{ scope.$index+1 }}</template>
-              </el-table-column>
-              <el-table-column prop="scheduleName" label="排班表名称" header-align="center" align="center"></el-table-column>
-              <el-table-column prop="periodStart" label="排班周期开始日期" header-align="center" align="center"></el-table-column>
-              <el-table-column prop="periodEnd" label="排班周期结束日期" header-align="center" align="center"></el-table-column>
-              <!-- <el-table-column prop="filePath" label="用户上传文件路径" header-align="center" align="center"></el-table-column> -->
-              <el-table-column prop="status" label="是否确认" header-align="center" align="center">
-                <template v-slot="scope">
-                  {{ state.getDictLabel("scheduleStatus", scope.row.status) }}
-                </template>
-              </el-table-column>
-              <el-table-column prop="creatorName" label="创建人" header-align="center" align="center"></el-table-column>
-              <el-table-column prop="createDate" label="创建时间" header-align="center" align="center"></el-table-column>
-              <el-table-column prop="updaterName" label="更新人" header-align="center" align="center"></el-table-column>
-              <el-table-column prop="updateDate" label="更新时间" header-align="center" align="center"></el-table-column>
-              <el-table-column prop="remark" label="备注" header-align="center" align="center"></el-table-column>
-            <el-table-column label="操作" fixed="right" header-align="center" align="center" width="180">
-        <template v-slot="scope">
-          <el-button v-if="state.hasPermission('emergency:schedule:update')" type="primary" link @click="addOrUpdateHandle(scope.row.id)">修改</el-button>
-          <el-button v-if="state.hasPermission('emergency:schedule:delete')" type="primary" link @click="state.deleteHandle(scope.row.id)">删除</el-button>
-           <el-button v-if="state.hasPermission('emergency:schedule:review')" type="primary" link @click="reviewHandle(scope.row.id)">审阅</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
+    <!-- <el-calendar v-model="calendarValue" /> -->
+    <el-calendar v-model="calendarValue">
+      <template #date-cell="{ data }">
+        <div
+          class="calendar-cell"
+          @dblclick="handleDayDblClick(data.day)"
+        >
+        <el-checkbox
+          :label="formatMonthDay(data.day)"
+          :value="data.day"
+          v-model="selectedDates"
+          @click.stop 
+        />
+        <div class="employee-names">
+          <div v-for="(name, index) in (scheduleMap[data.day]?.names || [])" :key="index" class="employee-name" @dblclick.stop>
+            {{ name }}
+          </div>
+        </div>
+        </div>
+      </template>
+    </el-calendar>
+
     <el-pagination :current-page="state.page" :page-sizes="[10, 20, 50, 100]" :page-size="state.limit" :total="state.total" layout="total, sizes, prev, pager, next, jumper" @size-change="state.pageSizeChangeHandle" @current-change="state.pageCurrentChangeHandle"> </el-pagination>
     <!-- 弹窗, 新增 / 修改 -->
-    <add-or-update ref="addOrUpdateRef" @refreshDataList="state.getDataList">确定</add-or-update>
+    <add-or-update ref="addOrUpdateRef" @refreshDataList="refreshAfterAdd">确定</add-or-update>
   </div>
 </template>
 
@@ -99,9 +70,85 @@ const view = reactive({
 
 const state = reactive({ ...useView(view), ...toRefs(view) });
 
+//日历
+const calendarValue = ref(new Date())
+const selectedDates = ref<string[]>([]);
+const formatMonthDay = (fullDate: string): string => {
+  const parts = fullDate.split("-");
+  return `${parts[1]}-${parts[2]}`;
+};
+
+const employeeOptions = ref<{ id: string; name: string }[]>([]);
+// id->姓名
+const employeeIdMap = ref<Record<string, string>>({});
+// 日期->员工姓名数组
+const scheduleMap = ref<Record<string, { id: number; names: string[] }>>({});
+
+
+// 构建id->姓名映射
+const fetchEmployeeOptions = async () => {
+  const res = await baseService.get("/emergency/employee/page");
+  employeeOptions.value = res.data.list || [];
+  
+  employeeIdMap.value = {};
+  for (const item of employeeOptions.value) {
+    employeeIdMap.value[item.id] = item.name;
+  }
+};
+const buildScheduleMap = (list: any[]) => {
+  scheduleMap.value = {};
+  for (const item of list) {
+    const day = item.scheduleDate; 
+    //保存的ids
+    const ids = item.employeeIds || [];
+    const names = ids.map((id: string) => employeeIdMap.value[id] || `员工姓名未知,id为${id}`);
+    scheduleMap.value[day] = { id: item.id, names};
+  }
+};
+import { onMounted } from 'vue'
+onMounted(async () => {
+  await fetchEmployeeOptions();
+  await state.getDataList();
+  buildScheduleMap(state.dataList || []);
+});
+
+// 新增按钮
+const addHandle = () => {
+  if (!selectedDates.value || selectedDates.value.length === 0) {
+    ElMessage({
+      message: '请先选择一个或多个日期再新增排班',
+      type: 'warning',
+      duration: 500,
+    });
+    return;
+  }
+  // 批量添加
+  addOrUpdateHandle(undefined, undefined, selectedDates.value);
+};
+//新增后清除已选中
+const refreshAfterAdd = async () => {
+  await fetchEmployeeOptions();
+  await state.getDataList(); 
+  buildScheduleMap(state.dataList || []);       
+  selectedDates.value = [];  
+};
+
+
 const addOrUpdateRef = ref();
-const addOrUpdateHandle = (id?: number) => {
-  addOrUpdateRef.value.init(id);
+const addOrUpdateHandle = (id?: number, date?: string, dates?: string[]) => {
+  addOrUpdateRef.value.init(id, date, dates);
+};
+
+
+const handleDayDblClick = (day: string) => {
+  const schedule = scheduleMap.value[day];
+  if (schedule && schedule.id) {
+    // 已有排班记录,修改
+    addOrUpdateHandle(schedule.id);
+  } else {
+    // 不存在,调用“新增”
+    addOrUpdateHandle(undefined, day);
+  }
 };
 
 // 审阅
@@ -138,3 +185,29 @@ const reviewHandle = (id?: string) => {
     });
 };
 </script>
+<style>
+.calendar-cell {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center; 
+  justify-content: flex-start; 
+  padding-top: 6px;    
+  box-sizing: border-box;
+  padding: 4px;
+  cursor: pointer;
+}
+
+
+::v-deep(.el-calendar-day) {
+  display: flex !important;
+  flex-direction: column;
+  align-items: flex-start;
+  justify-content: flex-start;
+  padding: 4px;
+  box-sizing: border-box;
+}
+
+
+</style>