소스 검색

Merge branch 'page-config' into system-assemble

sunhongyao341504 2 년 전
부모
커밋
e3271eac14

+ 114 - 0
src/api/config/config.ts

@@ -0,0 +1,114 @@
+import { http } from '@/utils/http/axios';
+
+//删除公司主页布局
+export const delCompanyLayout = (layoutld: number) => {
+  return http.request({
+    url: `/homepageConfig/deleteCompanyLayout?layoutld=${layoutld}`,
+    method: 'DELETE',
+  });
+};
+// /homepageConfig/CLLaaegimnoopstttuyy;
+//   /homepageConfig/deleteCompanyLayout
+// Promise<SceneLabelByCompanyType[]>;
+
+export interface CompanyLayoutDetail {
+  id?: number;
+  viewType?: number;
+  targetId?: number;
+  name?: string;
+  labelId?: number;
+  layout?: number; //后面修改
+  version?: string;
+  status?: number;
+  createdAt?: string;
+  updatedAt?: string;
+  isDeleted?: number;
+}
+
+////查询公司主页布局
+export const getCompanyLayout = (params: string | null) => {
+  return http.request<CompanyLayoutDetail[]>({
+    url: `/homepageConfig/getCompanyLayoutList`,
+    method: 'get',
+    params,
+  });
+};
+
+// /homepageConfig/saveCompanyLayout
+
+export interface CompanyLayoutSave {
+  layout?: number; //后面修改
+  labelId?: number;
+  version?: string;
+  status?: number;
+  viewType?: number;
+  targetId?: number;
+  id?: number;
+}
+
+//新建公司主页布局
+export const saveCompanyLayout = (data: CompanyLayoutSave) => {
+  return http.request({
+    url: `/homepageConfig/saveCompanyLayout`,
+    method: 'post',
+    data,
+  });
+};
+
+//更新公司主页布局
+export const updateCompanyLayout = (data: CompanyLayoutSave) => {
+  return http.request({
+    url: `/homepageConfig/updateCompanyLayout`,
+    method: 'put',
+    data,
+  });
+};
+
+export interface WorkshopPic {
+  deleteFileName?: string;
+  workshopld?: number;
+  file?: File;
+}
+
+//上传车间缩略图   会返回图片的名字
+export const uploadWorkshopPicture = (data: WorkshopPic) => {
+  return http.request({
+    url: `/homepageConfig/uploadWorkshopPicture`,
+    method: 'post',
+    data,
+  });
+};
+
+//更新车间缩略图
+export const updateWorkshopPicture = (data: WorkshopPic) => {
+  return http.request({
+    url: `/homepageConfig/updateWorkshopPicture`,
+    method: 'post',
+    data,
+  });
+};
+
+export interface CompanyPic {
+  companyld?: number;
+  deleteFileName?: string;
+  labelld?: number;
+  file?: File;
+}
+
+//上传公司缩略图   会返回图片的名字
+export const uploadCompanyPicture = (data: CompanyPic) => {
+  return http.request({
+    url: `/homepageConfig//uploadCompanyPicture`,
+    method: 'post',
+    data,
+  });
+};
+
+//更新车间缩略图
+export const updateCompanyPicture = (data: CompanyPic) => {
+  return http.request({
+    url: `/homepageConfig/updateCompanyPicture`,
+    method: 'post',
+    data,
+  });
+};

BIN
src/assets/icons/back.png


BIN
src/assets/icons/config-fail.png


BIN
src/assets/icons/config-success.png


BIN
src/assets/icons/del.png


BIN
src/assets/icons/file.png


BIN
src/assets/icons/layout-left.png


BIN
src/assets/icons/layout-right.png


BIN
src/assets/icons/layout-top.png


BIN
src/assets/icons/slide-right.png


BIN
src/assets/icons/slide.png


BIN
src/assets/icons/upload.png


BIN
src/assets/icons/warn.png


+ 468 - 0
src/views/page-config/ConfigEidt.vue

@@ -0,0 +1,468 @@
+<template>
+  <div style="position: relative">
+    <div class="top-content">
+      <div style="display: flex; position: relative">
+        <img src="~@/assets/icons/back.png" alt="" @click="backPage" class="back-btn" />
+        <el-select
+          v-model="companySelet"
+          class="m-2"
+          placeholder="请选择相关公司"
+          style="width: 216px"
+          @change="changeCom"
+        >
+          <el-option
+            v-for="item in companyList"
+            :key="item.id"
+            :label="item.value"
+            :value="item.value"
+          />
+        </el-select>
+        <div v-if="companySelet" style="display: flex; margin-top: 8px">
+          <div class="label-workshop">选择标签:</div>
+          <div>
+            <el-radio-group v-model="label" size="10px" :border="true" style="display: flex">
+              <el-radio-button
+                v-for="item in labelList"
+                :key="item.id"
+                :label="item.id!"
+                class="label-select"
+                >{{ item.value }}</el-radio-button
+              >
+            </el-radio-group></div
+          >
+        </div>
+        <el-upload
+          ref="upload"
+          class="upload-demo"
+          :limit="1"
+          :show-file-list="false"
+          :on-exceed="handleExceed"
+          :on-change="onSelectfile"
+          :auto-upload="false"
+          :before-upload="beforeAvatarUpload"
+        >
+          <template #trigger>
+            <el-button :icon="Refresh" plain class="btn-top-refresh">替换照片</el-button>
+          </template>
+        </el-upload>
+        <el-button v-if="saveSwitch" type="info" plain class="btn-top-save" @click="saveConfigPage"
+          >保存为主页</el-button
+        >
+        <el-button v-else type="info" plain class="btn-top-save" @click="saveConfigMap"
+          >保存为地图</el-button
+        >
+        <!-- <div>按钮</div> -->
+      </div>
+    </div>
+    <div style="display: flex">
+      <div class="workshop-content">
+        <div class="workshop-title">车间列表</div>
+        <el-input
+          v-model="searchWorkshop"
+          class="w-50 m-2"
+          placeholder="请输入搜索内容"
+          :suffix-icon="Search"
+          style="width: 255px; margin-top: 11px"
+        />
+        <ul v-if="workshopList"
+          ><li
+            v-for="item in workshopList"
+            :key="item.id"
+            class="workshop-list"
+            :class="{ 'active-workshop': activeId === item.id }"
+            @click="configWorkshop(item)"
+            ><el-icon><House /></el-icon
+            ><div style="margin-left: 5px; margin-top: -4px">{{ item.value }}</div></li
+          ></ul
+        >
+        <div v-else class="workshop-tip">提示:请先选择相应公司和照片</div>
+      </div>
+      <div>
+        <div class="upload" :class="{ 'avatar-show': imageUrl }">
+          <el-upload
+            class="avatar-uploader"
+            :auto-upload="false"
+            :on-change="onSelectfile"
+            :before-upload="beforeAvatarUpload"
+          >
+            <template #trigger>
+              <img src="~@/assets/icons/upload.png" alt="" class="upload-pic" />
+            </template>
+          </el-upload>
+          <!-- <div class="upload-tip">只支持.jpg格式</div> -->
+        </div>
+
+        <img v-if="imageUrl" :src="imageUrl" class="preview-image" />
+        <!-- <div>222</div> -->
+      </div></div
+    >
+    <el-tooltip
+      class="box-item position-tooltip"
+      effect="dark"
+      content="显示侧边栏"
+      offset="12"
+      placement="left"
+      @click="showEditConfig"
+    >
+      <div
+        v-if="leftShow"
+        class="circle-rectangle"
+        :class="{ 'shape-shadow': shadow }"
+        @mouseover="shadowAdd"
+        @mouseout="shadowRemove"
+        @click="dialogReopen"
+      >
+        <el-icon class="left-icon" size="16px"><ArrowLeftBold /></el-icon>
+        <el-icon style="margin-top: 7px" size="16px"><ArrowLeftBold /></el-icon>
+        <!-- <el-icon class="left-icon"><DArrowLeft /></el-icon> -->
+      </div>
+      <!-- <img src="~@/assets/icons/slide.png" alt="" class="dialog-btn" /> -->
+    </el-tooltip>
+
+    <ConfigDialog
+      ref="configDrawer"
+      :title="configTitle"
+      @on-close="onClose"
+      @save-config="saveConfig"
+      class="drawer-position"
+    />
+    <ConfigFinish
+      :visible="visibleResult"
+      :status="configStatus"
+      @on-close="closeResult"
+      class="feedback-position"
+    />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { Refresh, Search, ArrowLeftBold, House } from '@element-plus/icons-vue';
+  import ConfigDialog from './component/ConfigDrawer.vue';
+  import ConfigFinish from './component/ConfigFinish.vue';
+  //   import { picList } from '../constant';
+  import { labelList, companyList, workshopList } from './constant';
+  import { ElMessage, genFileId } from 'element-plus';
+  import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+  // import upload, { UploadRawFile, genFileId } from 'element-plus/es/components/upload';
+  import { useRouter } from 'vue-router';
+  const router = useRouter();
+
+  // const props = defineProps<{
+  //   pageShow: boolean;
+  //   // workshopTemplateList: WorkshopModuleType[];
+  // }>();
+
+  //   const emit = defineEmits<{
+  //     // (e: 'update:modelValue'): unknown;
+  //     (e: 'onClose'): unknown;
+  //   }>();
+
+  const label = ref('');
+
+  const companySelet = ref('');
+
+  const searchWorkshop = ref('');
+
+  const saveSwitch = ref<boolean>(true);
+
+  const changeCom = () => {
+    saveSwitch.value = true;
+  };
+  const configStatus = ref(true);
+  // const replacePic = () => {};
+  const saveConfigPage = () => {
+    saveSwitch.value = false;
+    // router.push('/page-config/config');
+  };
+
+  //总体的保存,将整个数据传过去
+  const visibleResult = ref(false);
+  const saveConfigMap = () => {
+    //这里需要控制成功或者失败的弹出框
+    visibleResult.value = true;
+    //configStatus修改状态,决定成功还是失败
+    // saveSwitch.value = true;
+    // router.push('/page-config/config');
+  };
+
+  const imageUrl = ref('');
+  const configDrawer = ref();
+
+  const backPage = () => {
+    router.push('/page-config/layout');
+  };
+
+  const beforeAvatarUpload = (rawFile) => {
+    if (rawFile.type !== 'image/jpeg') {
+      ElMessage.error('Avatar picture must be JPG format!');
+      return false;
+    }
+    // else if (rawFile.size / 1024 / 1024 > 2) {
+    //   ElMessage.error('Avatar picture size can not exceed 2MB!');
+    //   return false;
+    // }
+    return true;
+  };
+
+  const onSelectfile = (uploadFile) => {
+    imageUrl.value = URL.createObjectURL(uploadFile.raw!);
+  };
+
+  const upload = ref<UploadInstance>();
+  const handleExceed: UploadProps['onExceed'] = (files) => {
+    upload.value!.clearFiles();
+    const file = files[0] as UploadRawFile;
+    file.uid = genFileId();
+    upload.value!.handleStart(file);
+  };
+
+  const activeId = ref();
+  const configTitle = ref();
+  const visibleDrawer = ref();
+  //编辑车间
+  const configWorkshop = (item) => {
+    configDrawer.value.openDialog();
+    visibleDrawer.value = true;
+    configTitle.value = item.value;
+    activeId.value = item.id;
+  };
+
+  const showEditConfig = () => {};
+  // const label = ref('');
+
+  const shadow = ref(false);
+  const shadowAdd = () => {
+    shadow.value = true;
+  };
+  const shadowRemove = () => {
+    shadow.value = false;
+  };
+
+  const dialogReopen = () => {
+    configDrawer.value.openDialog();
+  };
+
+  //左边的浮动按钮
+  const leftShow = ref(false);
+  const onClose = (val) => {
+    leftShow.value = val;
+  };
+
+  //需要从子组件中获得当前保存的车间数据
+  const saveConfig = (val) => {
+    console.log(val);
+  };
+
+  const closeResult = () => {
+    visibleResult.value = false;
+  };
+</script>
+
+<style lang="scss" scoped>
+  // .proCard {
+  //   padding: 0px;
+  // }
+  .top-content {
+    padding: 0px;
+    background-color: white;
+    position: relative;
+  }
+
+  .back-btn {
+    width: 32px;
+    height: 32px;
+    margin-top: 7px;
+    margin-right: 20px;
+  }
+
+  .btn-top-refresh {
+    position: absolute;
+    margin-top: 14px;
+    right: 137px;
+    // left: 1437px;
+  }
+
+  .btn-top-save {
+    position: absolute;
+    // margin-top: 8px;
+    margin-top: 6px;
+    right: 15px;
+  }
+
+  .label-workshop {
+    font-size: 14px;
+    font-weight: 400;
+    margin-left: 36px;
+    margin-top: 6px;
+    margin-right: 16px;
+  }
+
+  .workshop-content {
+    margin-top: 10px;
+    background-color: white;
+    // padding-left: 15px;
+    // margin-left: 10px;
+    margin-right: 10px;
+    width: 271px;
+    height: 800px;
+    border: 1px solid #b3b3b3;
+  }
+
+  .workshop-title {
+    font-size: 14px;
+    margin-top: 25px;
+    margin-left: 15px;
+    font-weight: 500;
+    color: rgba(0, 0, 0, 0.85);
+  }
+
+  // .label-select {
+  // }
+  // .el-radio-button__inner
+  ::v-deep.el-radio-button {
+    margin-right: 8px;
+    box-shadow: none;
+    border-radius: 4px !important;
+    border: 1px solid #d9d9d9 !important;
+  }
+  .workshop-list {
+    font-size: 14px;
+    width: 211px;
+    margin-top: 10px;
+    margin-left: 15px;
+    font-weight: 400;
+    color: rgba(0, 0, 0, 0.85);
+    cursor: pointer;
+    display: flex;
+  }
+
+  .workshop-tip {
+    margin-left: 15px;
+    margin-top: 12px;
+    font-size: 14px;
+    font-weight: 400;
+    color: rgba(0, 0, 0, 0.65);
+  }
+
+  .upload-pic {
+    z-index: 99;
+    width: 593px;
+    height: 435px;
+    // margin-left: ;
+  }
+  .active-workshop {
+    background: rgba(24, 144, 255, 0.502);
+  }
+
+  .avatar-uploader .el-upload {
+    border: 1px dashed var(--el-border-color);
+    border-radius: 6px;
+    width: 593px;
+    height: 435px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+    color: #8c939d;
+    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;
+  //   margin-top: -60px;
+  // }
+
+  .upload {
+    position: absolute;
+    left: 428px;
+    top: 174px;
+  }
+
+  // .upload-tip {
+  //   margin-top: 5px;
+  //   font-size: 22px;
+  //   font-weight: 350;
+  //   color: #3d3d3d;
+  //   opacity: 0.4;
+  //   text-align: center;
+  // }
+
+  .uploader-text {
+    margin-top: -60px;
+    margin-left: 50px;
+    font-size: 22px;
+    font-weight: 350;
+    color: #3d3d3d;
+    opacity: 0.4;
+  }
+
+  // .preview-container {
+  //   width: 178px;
+  //   height: 178px;
+  //   position: relative;
+  // }
+
+  .preview-image {
+    min-width: 100%;
+    min-height: 100%;
+    object-fit: contain;
+  }
+
+  .avatar-show {
+    display: none;
+  }
+
+  .drawer-position {
+    position: absolute;
+    right: 0px;
+    // left: 1150px;
+    top: 74px;
+    z-index: 99;
+    opacity: 1;
+    background: #ffffff;
+  }
+
+  .dialog-btn {
+    position: absolute;
+    right: 0px;
+    top: 66px;
+  }
+
+  .position-tooltip {
+    margin-right: -10px;
+  }
+
+  .circle-rectangle {
+    width: 40px;
+    height: 30px;
+    border-radius: 50% 0% 0% 50%;
+    position: absolute;
+    right: 0px;
+    top: 66px;
+    background-color: white;
+    display: flex;
+  }
+
+  .shape-shadow {
+    filter: drop-shadow(5px 5px 10px rgb(102, 100, 100));
+  }
+  .left-icon {
+    margin-top: 7px;
+    margin-left: 8px;
+    margin-right: -7px;
+  }
+
+  .feedback-position {
+    position: absolute;
+    left: 460px;
+    top: 150px;
+    z-index: 9999;
+  }
+</style>

+ 14 - 0
src/views/page-config/PageConfig.vue

@@ -0,0 +1,14 @@
+<template>
+  <el-card :bordered="false" class="proCard" style="position: relative">
+    <PageMain />
+    <!-- <ConfigEidt :pageShow="pageShow" /> -->
+  </el-card>
+</template>
+<script lang="ts" setup>
+  // import { ref } from 'vue';
+  import PageMain from './component/PageMain.vue';
+  // import ConfigEidt from './component/ConfigEidt.vue';
+  // const pageShow = ref<boolean>(true);
+</script>
+
+<style lang="scss" sctep></style>

+ 321 - 0
src/views/page-config/component/ConfigDrawer.vue

@@ -0,0 +1,321 @@
+<template>
+  <div
+    ><el-dialog
+      v-model="dialogTableVisible"
+      :modal="false"
+      :show-close="false"
+      draggable
+      :append-to-body="false"
+      :destroy-on-close="true"
+    >
+      <template #header>
+        <div style="position: relative">
+          <div class="dialog-header">{{ props.title }}</div>
+          <img
+            src="~@/assets/icons/slide-right.png"
+            alt=""
+            class="dialog-return"
+            @click="closeDialog"
+          />
+        </div>
+      </template>
+      <div class="config-content">
+        <div>
+          <div class="uploader-title">缩略图</div>
+          <el-upload
+            class="pic-uploader"
+            list-type="picture-card"
+            :auto-upload="false"
+            :show-file-list="false"
+            :on-change="onSelectfile"
+            :before-upload="beforeAvatarUpload"
+          >
+            <img v-if="imageUrl" :src="imageUrl" class="avatar" />
+            <!-- <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon> -->
+            <div v-else>
+              <el-icon class="avatar-upload-icon" size="24px"><Plus /></el-icon>
+              <div class="uploader-config-text">上传照片</div>
+            </div>
+          </el-upload>
+          <div class="upload-config-tip">只支持.jpg格式</div>
+          <div style="display: flex; text-align: center"
+            ><img src="~@/assets/icons/warn.png" style="width: 14px; height: 14px" alt="" />
+            <div class="upload-notice">上传车间内部情况的视频流截图</div></div
+          >
+
+          <!-- <div class="upload-tips">备注:车间内部情况的视频流截图</div> -->
+        </div>
+        <hr />
+        <div>
+          <div class="content-title"
+            ><div class="uploader-title">填充</div>
+            <el-icon class="refresh-right"><RefreshRight /></el-icon
+          ></div>
+          <div class="color-select"
+            ><el-color-picker v-model="color" size="small" /><div class="color-content">{{
+              color
+            }}</div></div
+          >
+        </div>
+        <hr />
+        <div>
+          <div class="content-title"
+            ><div class="uploader-title">文字</div>
+            <el-icon class="refresh-right"><RefreshRight /></el-icon
+          ></div>
+          <div style="display: flex">
+            <el-select v-model="fontSize" class="fontsize-select" style="width: 50px" size="small">
+              <el-option
+                v-for="(item, index) in fontSizeList"
+                :key="index"
+                :label="item"
+                :value="item"
+              />
+            </el-select>
+            <div class="color-fontsize-select"
+              ><el-color-picker v-model="fontSizeColor" size="small" /><div
+                class="color-fontSize-content"
+                >{{ fontSizeColor }}</div
+              ></div
+            >
+          </div>
+          <hr />
+          <div>
+            <div class="content-title"><div class="uploader-title">布局</div> </div>
+            <div class="layout-set">
+              <img src="~@/assets/icons/layout-left.png" alt="" style="margin-right: 19px" />
+              <img src="~@/assets/icons/layout-right.png" alt="" style="margin-right: 17px" />
+              <img src="~@/assets/icons/layout-top.png" alt="" />
+            </div>
+          </div>
+        </div>
+      </div>
+      <el-button type="primary" class="save-dialog" @click="saveWorkshopConfig">保存</el-button>
+    </el-dialog>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { Close, Plus, RefreshRight } from '@element-plus/icons-vue';
+  import { ElMessage } from 'element-plus';
+
+  const props = defineProps<{
+    // visible: boolean;
+    title: string;
+    // workshopTemplateList: WorkshopModuleType[];
+  }>();
+
+  const emit = defineEmits(['onClose', 'saveConfig']);
+
+  // const emit = defineEmits<{
+  //   // (e: 'update:modelValue'): unknown;
+  //   (e: 'onClose'): unknown;
+  // }>();
+
+  // const closeDrawer = () => {
+  //   emit('onClose');
+  // };
+  const color = ref('');
+  const dialogTableVisible = ref(false);
+  const imageUrl = ref('');
+
+  const onSelectfile = (uploadFile) => {
+    imageUrl.value = URL.createObjectURL(uploadFile.raw!);
+  };
+  // const handleAvatarSuccess = () => {};
+
+  const beforeAvatarUpload = (rawFile) => {
+    if (rawFile.type !== 'image/jpeg') {
+      ElMessage.error('Avatar picture must be JPG format!');
+      return false;
+    }
+    // else if (rawFile.size / 1024 / 1024 > 2) {
+    //   ElMessage.error('Avatar picture size can not exceed 2MB!');
+    //   return false;
+    // }
+    return true;
+  };
+
+  const openDialog = () => {
+    dialogTableVisible.value = !dialogTableVisible.value;
+  };
+
+  const closeDialog = () => {
+    dialogTableVisible.value = false;
+    emit('onClose', true);
+  };
+
+  const fontSize = ref<number>(14);
+  const fontSizeList = Array.from({ length: 11 }, (_, index) => index + 10);
+
+  const fontSizeColor = ref('');
+
+  const saveWorkshopConfig = () => {
+    //这里需要传入数据
+    emit('saveConfig', {});
+  };
+
+  defineExpose({ openDialog });
+</script>
+
+<style lang="scss" scoped>
+  :deep(.el-dialog) {
+    width: 271px !important;
+    height: 800px !important;
+    position: absolute;
+    right: 0px;
+    top: -10px;
+    // height: 660px !important;
+  }
+
+  .dialog-header {
+    font-size: 16px;
+  }
+  .dialog-return {
+    position: absolute;
+    right: -10px;
+    top: 0px;
+    cursor: pointer;
+  }
+
+  :deep(.el-dialog__headerbtn .el-dialog__close) {
+    display: none;
+  }
+  // ::v-deep.el-dialog__headerbtn .el-dialog__close {
+  //   display: none;
+  // }
+
+  .drawer-box {
+    width: 251px;
+    height: 711px;
+  }
+
+  :deep(.el-upload--picture-card) {
+    margin-left: 50px;
+    width: 104px;
+    height: 104px;
+  }
+  .pic-uploader .el-upload {
+    border: 1px dashed var(--el-border-color);
+    border-radius: 6px;
+    width: 104px;
+    height: 104px;
+    cursor: pointer;
+    // margin-top: 24px;
+    margin-left: 0px;
+    position: relative;
+    overflow: hidden;
+    transition: var(--el-transition-duration-fast);
+    // text-align: center;
+  }
+
+  //   .avatar-uploader .el-upload:hover {
+  //     border-color: var(--el-color-primary);
+  //   }
+  .uploader-title {
+    font-size: 12px;
+    margin-bottom: 8px;
+  }
+
+  .el-icon.avatar-upload-icon {
+    font-size: 18px;
+    color: black;
+    width: 104px;
+    height: 104px;
+    text-align: center;
+    margin-top: -60px;
+    margin-bottom: 10px;
+  }
+
+  .uploader-config-text {
+    margin-top: -45px;
+    margin-left: 26px;
+    font-size: 14px;
+    font-weight: 400;
+    color: rgba(0, 0, 0, 0.88);
+    // font-weight: 350;
+    // color: #3d3d3d;
+    // opacity: 0.4;
+  }
+  .upload-config-tip {
+    text-align: center;
+    font-size: 14px;
+    font-weight: 400;
+    color: rgba(0, 0, 0, 0.45);
+    // line-height: 22px;
+    margin-left: -20px;
+    margin-top: 8px;
+    margin-bottom: 8px;
+  }
+
+  .upload-notice {
+    font-size: 12px;
+    font-weight: 400;
+    color: rgba(0, 0, 0, 0.45);
+    margin-left: 4px;
+    margin-bottom: 16px;
+    // text-align: center;
+    // margin-top: 10px;
+  }
+  // .upload-tips {
+  //   font-size: 16px;
+  //   text-align: center;
+  // }
+
+  .content-title {
+    display: flex;
+    margin-top: 16px;
+    margin-bottom: 8px;
+  }
+
+  .refresh-right {
+    margin-left: 4px;
+    margin-top: 2px;
+  }
+
+  .color-select {
+    display: flex;
+    margin-left: 34px;
+    margin-bottom: 16px;
+  }
+  .color-content {
+    width: 120px;
+    height: 24px;
+    line-height: 24px;
+    padding-left: 10px;
+    // margin-top: 8px;
+    background: rgba(0, 0, 0, 0.04);
+  }
+
+  .fontsize-select {
+    margin-left: 34px;
+  }
+  .color-fontSize-content {
+    width: 74px;
+    height: 24px;
+    line-height: 24px;
+    padding-left: 10px;
+    // margin-top: 8px;
+    background: rgba(0, 0, 0, 0.04);
+  }
+
+  .color-fontsize-select {
+    display: flex;
+    // margin-left: 34px;
+    margin-bottom: 16px;
+  }
+  .layout-set {
+    width: 156px;
+    height: 24px;
+    background: #f5f5f5;
+    border-radius: 2px;
+    margin-left: 34px;
+    display: flex;
+  }
+
+  .save-dialog {
+    position: absolute;
+    left: 105px;
+    bottom: 35px;
+  }
+</style>

+ 298 - 0
src/views/page-config/component/ConfigEidt.vue

@@ -0,0 +1,298 @@
+<template>
+  <div v-if="!props.pageShow" style="position: relative"
+    ><div style="display: flex; position: relative">
+      <!-- <el-input
+        v-model="searchCom"
+        class="w-50 m-2"
+        placeholder="搜索公司主页"
+        :prefix-icon="Search"
+        style="width: 274px"
+      /> -->
+      <el-select v-model="companySelet" class="m-2" placeholder="搜索公司" style="width: 274px">
+        <el-option
+          v-for="item in companyList"
+          :key="item.id"
+          :label="item.value"
+          :value="item.value"
+        />
+      </el-select>
+      <div v-if="companySelet" style="display: flex; margin-top: 8px">
+        <div style="font-size: 20px; margin-left: 29px; margin-right: 4px">选择标签</div>
+        <div>
+          <el-radio-group v-model="label" size="10px" :border="true" style="display: flex">
+            <el-radio-button
+              v-for="item in labelList"
+              :key="item.id"
+              :label="item.id!"
+              class="label-select"
+              >{{ item.value }}</el-radio-button
+            >
+          </el-radio-group></div
+        >
+      </div>
+      <el-upload
+        ref="upload"
+        class="upload-demo"
+        :limit="1"
+        :show-file-list="false"
+        :on-exceed="handleExceed"
+        :on-change="onSelectfile"
+        :auto-upload="false"
+        :before-upload="beforeAvatarUpload"
+      >
+        <template #trigger>
+          <el-button :icon="Refresh" plain class="btn-top-refresh">替换照片</el-button>
+        </template>
+      </el-upload>
+      <!-- <el-button type="info" :icon="Refresh" plain class="btn-top-refresh" @click="replacePic"
+        >替换照片</el-button
+      > -->
+      <el-button type="info" plain class="btn-top-save" @click="saveConfig">保存</el-button>
+      <!-- <div>按钮</div> -->
+    </div>
+    <div style="display: flex">
+      <div class="workshop-content">
+        <div class="workshop-title">车间列表</div>
+        <el-input
+          v-model="searchWorkshop"
+          class="w-50 m-2"
+          placeholder="搜索功能"
+          :prefix-icon="Search"
+          style="width: 255px; margin-top: 11px"
+        />
+        <ul
+          ><li
+            v-for="item in workshopList"
+            :key="item.id"
+            class="workshop-list"
+            :class="{ 'active-workshop': activeId === item.id }"
+            @click="configWorkshop(item)"
+            >{{ item.value }}</li
+          ></ul
+        >
+      </div>
+      <div>
+        <div class="upload" :class="{ 'avatar-show': imageUrl }">
+          <el-upload
+            class="avatar-uploader"
+            list-type="picture-card"
+            :auto-upload="false"
+            :on-change="onSelectfile"
+            :before-upload="beforeAvatarUpload"
+          >
+            <!-- list-type="picture-card" -->
+            <!-- <div class="el-upload__text"> Drop file here or </div> -->
+            <!-- <img v-if="imageUrl" :src="imageUrl" class="avatar" /> -->
+            <div>
+              <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
+              <div class="uploader-text">上传照片</div>
+            </div>
+            <!-- <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon> -->
+          </el-upload>
+          <div class="upload-tip">只支持.jpg格式</div></div
+        >
+
+        <img v-if="imageUrl" :src="imageUrl" class="preview-image" />
+        <!-- <div>222</div> -->
+      </div></div
+    >
+    <ConfigDrawer
+      :visible="visibleDrawer"
+      :title="configTitle"
+      :onClose="onClose"
+      class="drawer-position"
+    />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { Refresh, Search, Delete, Plus } from '@element-plus/icons-vue';
+  import ConfigDrawer from './ConfigDrawer.vue';
+  import { picList } from '../constant';
+  import { labelList, companyList, workshopList } from '../constant';
+  import { ElMessage, genFileId } from 'element-plus';
+  import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+  // import upload, { UploadRawFile, genFileId } from 'element-plus/es/components/upload';
+
+  const props = defineProps<{
+    pageShow: boolean;
+    // workshopTemplateList: WorkshopModuleType[];
+  }>();
+
+  const emit = defineEmits<{
+    // (e: 'update:modelValue'): unknown;
+    (e: 'onClose'): unknown;
+  }>();
+
+  const label = ref('');
+
+  const companySelet = ref('');
+
+  const searchWorkshop = ref('');
+
+  // const replacePic = () => {};
+  const saveConfig = () => {};
+
+  const imageUrl = ref('');
+
+  const beforeAvatarUpload = (rawFile) => {
+    if (rawFile.type !== 'image/jpeg') {
+      ElMessage.error('Avatar picture must be JPG format!');
+      return false;
+    }
+    // else if (rawFile.size / 1024 / 1024 > 2) {
+    //   ElMessage.error('Avatar picture size can not exceed 2MB!');
+    //   return false;
+    // }
+    return true;
+  };
+
+  const onSelectfile = (uploadFile) => {
+    console.log('2333');
+
+    imageUrl.value = URL.createObjectURL(uploadFile.raw!);
+    console.log('12333');
+  };
+
+  const upload = ref<UploadInstance>();
+  const handleExceed: UploadProps['onExceed'] = (files) => {
+    upload.value!.clearFiles();
+    const file = files[0] as UploadRawFile;
+    file.uid = genFileId();
+    upload.value!.handleStart(file);
+  };
+
+  const activeId = ref();
+  const configTitle = ref();
+  const visibleDrawer = ref();
+  //编辑车间
+  const configWorkshop = (item) => {
+    visibleDrawer.value = true;
+    configTitle.value = item.value;
+    activeId.value = item.id;
+  };
+  const leftShow = ref(false);
+  const onClose = (value) => {
+    leftShow.value = value;
+  };
+  // const label = ref('');
+</script>
+
+<style lang="scss" scoped>
+  .btn-top-refresh {
+    position: absolute;
+    margin-top: 19px;
+    right: 130px;
+    // left: 1437px;
+  }
+
+  .btn-top-save {
+    position: absolute;
+    margin-top: 8px;
+    right: 46px;
+  }
+
+  .workshop-content {
+    margin-top: 34px;
+    margin-left: 10px;
+    margin-right: 10px;
+    width: 271px;
+    height: 660px;
+    border: 1px solid #b3b3b3;
+  }
+
+  .workshop-title {
+    font-size: 20px;
+    margin-top: 15px;
+    margin-left: 13px;
+    font-weight: 900;
+    color: #3d3d3d;
+    opacity: 0.4;
+  }
+
+  .workshop-list {
+    font-size: 20px;
+    width: 211px;
+    margin-top: 15px;
+    margin-left: 13px;
+    font-weight: 350;
+    color: #3d3d3d;
+    opacity: 0.4;
+    cursor: pointer;
+  }
+  .active-workshop {
+    background: rgba(24, 144, 255, 0.502);
+  }
+
+  .avatar-uploader .el-upload {
+    border: 1px dashed var(--el-border-color);
+    border-radius: 6px;
+    width: 260px;
+    height: 280px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+    color: #8c939d;
+    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;
+    margin-top: -60px;
+  }
+
+  .upload {
+    position: absolute;
+    left: 635px;
+    top: 201px;
+  }
+
+  .upload-tip {
+    margin-top: 5px;
+    font-size: 22px;
+    font-weight: 350;
+    color: #3d3d3d;
+    opacity: 0.4;
+    text-align: center;
+  }
+
+  .uploader-text {
+    margin-top: -60px;
+    margin-left: 50px;
+    font-size: 22px;
+    font-weight: 350;
+    color: #3d3d3d;
+    opacity: 0.4;
+  }
+
+  // .preview-container {
+  //   width: 178px;
+  //   height: 178px;
+  //   position: relative;
+  // }
+
+  .preview-image {
+    min-width: 100%;
+    min-height: 100%;
+    object-fit: contain;
+  }
+
+  .avatar-show {
+    display: none;
+  }
+
+  .drawer-position {
+    position: absolute;
+    // left: 1603px;
+    right: 0px;
+    top: 0px;
+  }
+</style>

+ 107 - 0
src/views/page-config/component/ConfigFinish.vue

@@ -0,0 +1,107 @@
+<template>
+  <div v-if="props.visible" class="result-feedback">
+    <el-icon class="close-btn" @click="closeResult"><Close /></el-icon>
+    <!-- 成功 -->
+    <div v-if="props.status">
+      <img src="~@/assets/icons/config-success.png" alt="" class="result-pic" />
+      <div class="success-word">保存成功</div>
+      <div class="success-go" @click="goMain">前往查看</div>
+      <div class="success-back" @click="goBack">5S后返回首页</div>
+    </div>
+    <!-- 失败 -->
+    <div v-else>
+      <img src="~@/assets/icons/config-fail.png" alt="" class="result-pic" />
+      <div class="success-word">保存失败</div>
+      <div class="success-go" @click="reSave">重新上传</div>
+    </div>
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { Close, Plus, RefreshRight } from '@element-plus/icons-vue';
+  import { ElMessage } from 'element-plus';
+  import { useRouter } from 'vue-router';
+  const router = useRouter();
+
+  const props = defineProps<{
+    visible: boolean;
+    status: boolean;
+  }>();
+
+  const emit = defineEmits(['onClose', 'saveConfig']);
+
+  // const emit = defineEmits<{
+  //   // (e: 'update:modelValue'): unknown;
+  //   (e: 'onClose'): unknown;
+  // }>();
+
+  // const closeDrawer = () => {
+  //   emit('onClose');
+  // };
+  const closeResult = () => {
+    emit('onClose');
+  };
+
+  const goMain = () => {
+    router.push('/page-config/layout');
+  };
+
+  const goBack = () => {
+    router.push('/');
+  };
+
+  const reSave = () => {};
+</script>
+
+<style lang="scss" scoped>
+  .result-feedback {
+    width: 480px;
+    height: 460px;
+    // background: linear-gradient(
+    //   180deg,
+    //   #a8caea 1%,
+    //   rgba(24, 144, 255, 0.2) 40%,
+    //   rgba(148, 182, 245, 0) 100%
+    // );
+    background: linear-gradient(to bottom, #a8caea, white);
+    border-radius: 8px;
+    position: relative;
+  }
+  .close-btn {
+    position: absolute;
+    right: 24px;
+    top: 23px;
+  }
+  .result-pic {
+    margin-top: 39px;
+    margin-left: 166px;
+    margin-bottom: 20px;
+  }
+  .success-word {
+    font-size: 32px;
+    // font-family: YouSheBiaoTiHei;
+    color: rgba(0, 0, 0, 0.65);
+    text-align: center;
+    margin-bottom: 40px;
+  }
+  .success-go {
+    width: 200px;
+    height: 46px;
+    background: #1890ff;
+    color: #ffffff;
+    border-radius: 4px;
+    text-align: center;
+    font-size: 20px;
+    line-height: 46px;
+    margin-left: 140px;
+    cursor: pointer;
+    margin-bottom: 12px;
+  }
+  .success-back {
+    font-size: 14px;
+    font-weight: 400;
+    color: #1890ff;
+    text-align: center;
+    cursor: pointer;
+  }
+</style>

+ 174 - 0
src/views/page-config/component/PageMain.vue

@@ -0,0 +1,174 @@
+<template>
+  <div
+    ><div style="display: flex">
+      <el-input
+        v-model="searchCom"
+        class="search-btn w-50 m-2"
+        placeholder="搜索公司主页"
+        :suffix-icon="Search"
+      />
+    </div>
+    <div class="add-page-box">
+      <div class="add-config">
+        <div class="add-box" @click="handleAddPage"
+          ><div
+            ><el-icon class="add-icon" size="24px"><Plus /></el-icon></div
+          ><div class="add-content">新建主页</div></div
+        ></div
+      >
+
+      <div
+        v-for="item in picList"
+        :key="item.id"
+        class="content-show"
+        @mouseover="showDeleteIcon(item)"
+        @mouseleave="hideDeleteIcon(item)"
+      >
+        <div class="pic-box"><img src="" alt="图片" class="content-pic" /></div>
+
+        <div class="pic-word">
+          <img src="~@/assets/icons/file.png" alt="" />
+          <div class="pic-name">{{ item.name }}</div>
+          <img
+            v-show="item.id === IconId"
+            src="~@/assets/icons/del.png"
+            alt=""
+            @click="deleteItem(item)"
+            class="del-icon"
+          />
+          <!-- <el-icon v-show="item.id === IconId" @click="deleteItem(item)" class="del-icon"
+            ><Delete
+          /></el-icon> -->
+        </div>
+      </div>
+    </div></div
+  >
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { Search, Plus, Delete } from '@element-plus/icons-vue';
+  import { picList } from '../constant';
+  import { useRouter } from 'vue-router';
+  const router = useRouter();
+
+  const searchCom = ref('');
+  // const pageAdd = ref<boolean>(true);
+  const handleAddPage = () => {
+    router.push('/page-config/config');
+  };
+
+  const IconId = ref('');
+  const showDeleteIcon = (item) => {
+    IconId.value = item.id;
+  };
+
+  const hideDeleteIcon = (_item) => {
+    IconId.value = '';
+  };
+
+  const deleteItem = (item) => {
+    // 处理删除逻辑
+    console.log(item);
+  };
+
+  // const label = ref('');
+</script>
+
+<style lang="scss" sctep>
+  .search-btn {
+    width: 340px;
+    height: 32px;
+    background: #f0f2f5;
+    border-radius: 6px;
+  }
+
+  .add-page-box {
+    padding-top: 2px;
+    padding-left: 8px;
+    display: flex;
+    height: 770px;
+    flex-wrap: wrap;
+    margin-bottom: 24px;
+    align-content: flex-start;
+  }
+
+  .add-config {
+    width: 216px;
+    height: 191px;
+    margin-right: 26px;
+  }
+
+  .add-box {
+    width: 104px;
+    height: 104px;
+    background: rgba(255, 255, 255, 0.04);
+    border-radius: 5px;
+    border: 1px solid rgba(0, 0, 0, 0.15);
+    // text-align: center;
+    margin-left: 56px;
+    margin-top: 43px;
+  }
+
+  .add-icon {
+    margin-left: 40px;
+    margin-top: 24px;
+  }
+
+  .add-content {
+    font-size: 14px;
+    margin-top: 10px;
+    text-align: center;
+  }
+
+  .config-box {
+    height: 700px;
+  }
+
+  .label-select {
+    margin-right: 17px;
+    border-radius: 4px;
+    // border: 1px solid rgba(0, 0, 0, 0.15);
+  }
+
+  .content-show {
+    // display: flex;
+    width: 216px;
+    height: 191px;
+    margin-left: 26px;
+    border: 1px solid #e8ecf2;
+    margin-bottom: 24px;
+  }
+
+  .pic-box {
+    height: 146px;
+    width: 216px;
+    background: #e8ecf2;
+    padding-left: 17px;
+    padding-top: 17px;
+  }
+
+  .content-pic {
+    // margin-left: 17px;
+    // margin-top: 17px;
+    width: 182px;
+    height: 114px;
+  }
+
+  .pic-word {
+    position: relative;
+    display: flex;
+    padding-left: 16px;
+    padding-top: 13px;
+  }
+  .pic-name {
+    margin-left: 8px;
+    margin-top: 2px;
+    font-size: 12px;
+  }
+
+  .del-icon {
+    position: absolute;
+    right: 17px;
+    bottom: 3px;
+  }
+</style>

+ 30 - 0
src/views/page-config/constant.ts

@@ -0,0 +1,30 @@
+export const labelList = [
+  { id: 1, value: '标签1' },
+  { id: 2, value: '标签2' },
+  { id: 3, value: '标签3' },
+];
+
+export const picList = [
+  { id: '1', url: '11', name: '222' },
+  { id: '2', url: '11', name: '333' },
+  { id: '3', url: '11', name: '222' },
+  { id: '4', url: '11', name: '333' },
+  { id: '5', url: '11', name: '222' },
+  { id: '6', url: '11', name: '333' },
+  { id: '7', url: '11', name: '222' },
+  { id: '8', url: '11', name: '333' },
+  { id: '9', url: '11', name: '222' },
+  { id: '10', url: '11', name: '333' },
+];
+
+export const companyList = [
+  { id: 1, value: '商飞' },
+  { id: 2, value: '上飞厂' },
+  { id: 3, value: '客服' },
+];
+
+export const workshopList = [
+  { id: 1, value: '车间1' },
+  { id: 2, value: '车间2' },
+  { id: 3, value: '车间3' },
+];

+ 11 - 2
src/views/system/user/component/AddUser.vue

@@ -11,15 +11,16 @@
         <el-upload
           ref="upload"
           class="upload-demo"
+          :multiple="false"
+          :limit="1"
           drag
           action="http://localhost:8092/api/user/import"
           :headers="headers"
           :with-credentials="true"
           :auto-upload="false"
+          :on-exceed="handleExceed"
           :before-upload="beforeUpload"
           :on-success="handleUploadSuccess"
-          :on-exceed="handleExceed"
-          :limit="1"
           style="width: 384px; height: 192px; border-radius: 8px"
         >
           <el-icon class="el-icon--upload" style="width: 33px; height: 42px"><Document /></el-icon>
@@ -151,6 +152,14 @@
       target: '_self',
     });
   };
+
+  // const handleExceed: UploadProps['onExceed'] = (files) => {
+  //   upload.value!.clearFiles();
+  //   const file = files[0] as UploadRawFile;
+  //   file.uid = genFileId();
+  //   upload.value!.handleStart(file);
+  // };
+
   const handleImport = async () => {
     upload.value!.submit();
   };