Procházet zdrojové kódy

客户、供应商资质新增流程页面

Z před 1 rokem
rodič
revize
6e9d3f94e8

+ 1 - 0
package.json

@@ -44,6 +44,7 @@
     "vue": "^2.7.10",
     "vue-clipboard2": "^0.3.3",
     "vue-countup-v2": "^4.0.0",
+    "vue-cropper": "^0.6.5",
     "vue-echarts": "^6.2.3",
     "vue-i18n": "^8.27.2",
     "vue-router": "^3.6.4",

+ 61 - 0
src/api/bpm/components/qualification/index.js

@@ -0,0 +1,61 @@
+import request from '@/utils/request';
+
+
+// 资质列表查询
+export async function getProfessionCertificationPageList(params) {
+  const res = await request.get(`/main/professionCertification/page`, {
+    params
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 新增资质
+export async function saveProfessionCertification(data) {
+  const res = await request.post(`/main/professionCertification/save`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 通过id查询资质详情
+export async function getProfessionCertificationById(id) {
+  const res = await request.get(`/main/professionCertification/getById/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 删除资质
+export async function deleteProfessionCertificationById(data) {
+  const res = await request.delete(`/main/professionCertification/delete`, {
+    data
+  });
+  if (res.data.code == 0) {
+    return res.data.message;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 修改资质
+export async function updateProfessionCertificationById(data) {
+  const res = await request.put(`/main/professionCertification/update`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+/**
+ * 提交客户证书资质信息  开启流程
+ */
+export async function contactQcSubmit(data) {
+  const res = await request.post(`/bpm/ProfessionCertification/submit`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 15 - 7
src/layout/components/header-tools.vue

@@ -22,7 +22,11 @@
       <el-dropdown @command="onUserDropClick">
         <div class="ele-admin-header-avatar">
           <el-avatar
-            :src="loginUser && loginUser.avatar ? loginUser.avatar : ''"
+            :src="
+              loginUser && loginUser.avatarAddress
+                ? loginUser.avatarAddress
+                : xyy
+            "
           />
           <span class="hidden-xs-only">{{
             loginUser && loginUser.nickname ? loginUser.nickname : ''
@@ -31,6 +35,9 @@
         </div>
         <template v-slot:dropdown>
           <el-dropdown-menu>
+            <!--            <router-link to="/user/profile">-->
+            <!--              <el-dropdown-item icon="el-icon-user">个人中心</el-dropdown-item>-->
+            <!--            </router-link>-->
             <el-dropdown-item command="profile" icon="el-icon-user">
               {{ $t('layout.header.profile') }}
             </el-dropdown-item>
@@ -66,15 +73,16 @@
   import I18nIcon from './i18n-icon.vue';
   import { logout } from '@/utils/page-tab-util';
   import { userLogout } from '@/api/system/user';
-
+  import xyy from '@/assets/xyy.jpg';
   export default {
     components: { HeaderNotice, PasswordModal, SettingDrawer, I18nIcon },
     props: {
       // 是否是全屏
       fullscreen: Boolean
     },
-    data () {
+    data() {
       return {
+        xyy,
         // 是否显示修改密码弹窗
         passwordVisible: false,
         // 是否显示主题设置抽屉
@@ -83,13 +91,13 @@
     },
     computed: {
       // 当前用户信息
-      loginUser () {
+      loginUser() {
         return this.$store.state.user.info;
       }
     },
     methods: {
       /* 用户信息下拉点击事件 */
-      onUserDropClick (command) {
+      onUserDropClick(command) {
         if (command === 'password') {
           this.passwordVisible = true;
         } else if (command === 'profile') {
@@ -113,11 +121,11 @@
         }
       },
       /* 全屏切换 */
-      toggleFullscreen () {
+      toggleFullscreen() {
         this.$emit('fullscreen');
       },
       /* 打开设置抽屉 */
-      openSetting () {
+      openSetting() {
         this.settingVisible = true;
       }
     }

+ 610 - 0
src/views/bpm/handleTask/components/certificateQualifications/certificateQualificationsDialog.vue

@@ -0,0 +1,610 @@
+<template>
+ <div>
+   <el-form ref="form" :model="form" :rules="rules" class="el-form-box">
+     <headerTitle title="基本信息"/>
+     <el-row :gutter="20">
+       <!--        <el-col :span="12">-->
+       <!--          <el-form-item label="编码:" prop="code" label-width="90px">-->
+       <!--            <el-input-->
+       <!--              :disabled="type=='view'"-->
+       <!--              v-model="form.code"-->
+       <!--              clearable-->
+       <!--              placeholder="请输入"-->
+       <!--            />-->
+       <!--          </el-form-item>-->
+       <!--        </el-col>-->
+       <el-col :span="12">
+         <el-form-item label="名称" prop="name"
+                       label-width="90px"
+                       :rules=" {required: true, message: '请输入',trigger: 'blur' }">
+           <el-input v-model="form.name" :disabled="type=='view'" clearable></el-input>
+         </el-form-item>
+       </el-col>
+       <el-col :span="12">
+         <el-form-item label="有效时间:" prop="date" label-width="90px">
+           <el-date-picker
+             :disabled="type=='view'"
+             v-model="form.date"
+             style="width: 100%;"
+             type="daterange"
+             value-format="yyyy-MM-dd"
+             range-separator="至"
+             start-placeholder="开始日期"
+             end-placeholder="结束日期"
+           >
+           </el-date-picker>
+         </el-form-item>
+       </el-col>
+
+     </el-row>
+     <el-row :gutter="20">
+       <el-col :span="12">
+         <el-form-item label="资质类型:" prop="certificationType" label-width="90px">
+           <el-select
+             style="width: 100%"
+             :disabled="type=='view'"
+             v-model="form.certificationType"
+             @change="changeCertificationType"
+             filterable
+           >
+             <el-option
+               v-for="item in qualificationOptions"
+               :key="item.value"
+               :label="item.label"
+               :value="item.value"
+             >
+             </el-option>
+           </el-select>
+         </el-form-item>
+       </el-col>
+       <el-col :span="12">
+         <el-form-item label="关联类型:" prop="relationName" label-width="90px">
+           <el-input
+             :disabled="type=='view'"
+             v-model="form.relationName"
+             readonly
+             clearable
+             @click.native="handleClick"
+             placeholder="请选择"
+           />
+         </el-form-item>
+       </el-col>
+     </el-row>
+     <el-row :gutter="20">
+       <el-col :span="12">
+         <el-form-item label="备注" label-width="90px">
+           <el-input type="textarea" v-model="form.remark" :disabled="type=='view'" clearable></el-input>
+         </el-form-item>
+       </el-col>
+       <el-col :span="12">
+         <el-form-item label="附件:" prop="accessory" label-width="90px">
+           <fileUpload
+             v-if="type!=='view'"
+             v-model="form.accessory"
+             module="main"
+             :showLib="false"
+             :limit="10"/>
+           <div v-else>
+             <el-link
+               v-for="link in form.accessory"
+               :key="link.id"
+               type="primary"
+               :underline="false"
+               @click="downloadFile(link)">
+               {{ link.name }}
+             </el-link>
+           </div>
+         </el-form-item>
+       </el-col>
+     </el-row>
+     <headerTitle title="资质信息"/>
+     <ele-pro-table ref="linkTable" :columns="columns" :datasource="form.detailsList" :toolkit="[]" height="300px"
+                    :need-page="false">
+       <!-- 表头工具栏 -->
+       <template v-slot:toolbar>
+
+         <el-button v-if="type!=='view'" type="primary" @click="handleAdd">添加</el-button>
+       </template>
+       <template v-slot:name="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.name'" :rules="{
+            required: true,
+            message: '',
+            trigger: 'change'
+          }">
+           <el-select v-if="type!=='view'" v-model="scope.row.name" clearable>
+             <el-option :disabled="disabledToType(scope.row).includes(item.dictCode)"
+                        v-for="item in dictList" :value="item.dictCode"
+                        :label="item.dictValue"></el-option>
+           </el-select>
+           <!--                <DictSelection v-if="type!=='view'" clearable dictName="客户/供应商资质类型" v-model="scope.row.type"-->
+           <!--                               @itemChange="(val)=>handleChangeType(val,scope.row)"></DictSelection>-->
+           <span v-else>{{ getLabelName(dictList, scope.row.name) }}</span>
+         </el-form-item>
+
+       </template>
+       <template v-slot:code="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.code'" :rules="{
+            required: true,
+            message: '',
+            trigger: 'blur'
+          }">
+           <el-input v-model="scope.row.code" :disabled="type=='view'" clearable></el-input>
+         </el-form-item>
+
+       </template>
+       <template v-slot:businessRange="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.businessRange'">
+           <el-input type="textarea" v-model="scope.row.businessRange" :disabled="type=='view'" clearable></el-input>
+         </el-form-item>
+       </template>
+       <template v-slot:startTime="scope">
+         <el-form-item inline-message :prop="'detailsList.' + scope.$index + '.startTime'" :rules="{
+            required: true,
+            message: '',
+            trigger: 'change'
+
+          }">
+           <el-date-picker
+             :disabled="type=='view'"
+             v-model="scope.row.startTime"
+             type="date"
+             style="width: 100%"
+             value-format="yyyy-MM-dd"
+             placeholder="选择日期">
+           </el-date-picker>
+         </el-form-item>
+
+       </template>
+       <template v-slot:endTime="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.endTime'"
+                       :rules="{
+            required: true,
+            validator: validateEndDate(scope.row,scope.$index),
+            trigger: ['blur','change']
+          }">
+           <el-date-picker
+             :disabled="type=='view'"
+             v-model="scope.row.endTime"
+             type="date"
+             style="width: 100%"
+             value-format="yyyy-MM-dd"
+             placeholder="选择日期">
+           </el-date-picker>
+         </el-form-item>
+
+       </template>
+       <template v-slot:noticePersonName="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.noticePersonName'" :rules="{
+            required: true,
+            message: '',
+            trigger: ['blur','change']
+
+          }">
+           <el-input
+             :disabled="type=='view'"
+             @click.native="openStaffSelection(scope.$index)"
+             v-model="scope.row.noticePersonName"
+             placeholder="请选择"
+           ></el-input>
+         </el-form-item>
+
+       </template>
+       <template v-slot:accessory="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.accessory'" :rules="{
+            required: true,
+            message: '',
+            trigger: ['change','blur']
+          }">
+           <fileUpload
+             v-if="type!=='view'"
+             v-model="scope.row.accessory"
+             module="main"
+             :showLib="false"
+             :limit="10"/>
+           <div v-else>
+             <el-link
+               v-for="link in scope.row.accessory"
+               :key="link.id"
+               type="primary"
+               :underline="false"
+               @click="downloadFile(link)">
+               {{ link.name }}
+             </el-link>
+           </div>
+         </el-form-item>
+
+       </template>
+       <template v-slot:type="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.type'">
+           <el-select v-if="type!=='view'" v-model="scope.row.type" clearable>
+             <el-option v-for="item in typeList" :value="item.dictCode"
+                        :label="item.dictValue"></el-option>
+           </el-select>
+           <!--                <DictSelection v-if="type!=='view'" clearable dictName="客户/供应商资质类型" v-model="scope.row.type"-->
+           <!--                               @itemChange="(val)=>handleChangeType(val,scope.row)"></DictSelection>-->
+           <span v-else>{{ getLabelName(typeList, scope.row.type) }}</span>
+         </el-form-item>
+
+       </template>
+       <template v-slot:level="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.level'">
+           <el-select v-if="type!=='view'" v-model="scope.row.level" clearable>
+             <el-option v-for="item in levelOptions" :value="item.dictCode"
+                        :label="item.dictValue"></el-option>
+           </el-select>
+           <!--                <DictSelection v-if="type!=='view'" clearable dictName="客户/供应商资质类型" v-model="scope.row.type"-->
+           <!--                               @itemChange="(val)=>handleChangeType(val,scope.row)"></DictSelection>-->
+           <span v-else>{{ getLabelName(levelOptions, scope.row.type) }}</span>
+         </el-form-item>
+
+       </template>
+       <template v-slot:remark="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.remark'">
+           <el-input type="textarea" :disabled="type=='view'" v-model="scope.row.remark"></el-input>
+         </el-form-item>
+       </template>
+       <template v-slot:status="scope">
+         <el-form-item :prop="'detailsList.' + scope.$index + '.status'">
+           <el-tag v-if="scope.row.status" :type="statusTagTypeList[scope.row.status]">
+             {{ statusList[scope.row.status] }}
+           </el-tag>
+         </el-form-item>
+       </template>
+       <template v-slot:isRequired="{ column }">
+         <span class="is-required">{{ column.label }}</span>
+       </template>
+       <template v-slot:action="{ row, $index }">
+         <el-popconfirm
+           class="ele-action"
+           title="确定要删除该信息吗?"
+           @confirm="handleRemove($index)"
+         >
+           <template v-slot:reference>
+             <el-link
+               v-if="type!=='view'"
+               type="danger"
+               :underline="false"
+               icon="el-icon-delete"
+             >
+               删除
+             </el-link>
+           </template>
+         </el-popconfirm>
+       </template>
+
+     </ele-pro-table>
+
+   </el-form>
+ </div>
+
+</template>
+<script>
+import {
+  getProfessionCertificationById,
+  saveProfessionCertification,
+  updateProfessionCertificationById,
+  contactQcSubmit
+} from "@/api/bpm/components/qualification";
+import {getFile} from "@/api/system/file";
+import {mapActions, mapGetters} from "vuex";
+import dictEnum from "@/enum/dict";
+
+export default {
+  name: "addOrEditDialog",
+  components: {},
+  props:{
+    taskDefinitionKey: {
+      type: String,
+      default: 'starter',
+    },
+    businessId: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      title: '',
+      type: 'view',
+      qualificationOptions: [
+        {
+          label: '客户资质',
+          value: '1'
+        },
+        {
+          label: '供应商资质',
+          value: '2'
+        },
+        {
+          label: '个人资质',
+          value: '3'
+        }
+      ],
+      levelOptions: [
+        {
+          dictValue: '初级',
+          dictCode: '1'
+        },
+        {
+          dictValue: '中级',
+          dictCode: '2'
+        },
+        {
+          dictValue: '高级',
+          dictCode: '3'
+        }
+      ],
+      defaultData: {
+        accessory: [],
+        name: "",
+        noticePersonId: "",
+        noticePersonName: "",
+        num: "",
+        remark: "",
+        businessRange: "",
+        type: "",
+        endTime: "",
+        startTime: ""
+      },
+      form: {
+        accessory: [],
+        detailsList: [],
+        relationName: '',
+        name: '',
+        date: [],
+        remark: '',
+
+      },
+      rules: {
+        name: [{required: true, message: '请输入名称', trigger: 'blur'}],
+        code: [{required: true, message: '请输入编码', trigger: 'blur'}],
+        certificationType: [{required: true, message: '请选择资质类型', trigger: 'change'}],
+        relationName: [{required: true, message: '请选择关联类型', trigger: 'change'}],
+        date: [{required: true, message: '请选择有效时间', trigger: 'change'}],
+      },
+      curIndex: null,
+      StaffType: '',
+
+      statusList: {
+        10: '有效',
+        20: '无效',
+        30: '已过期',
+      },
+      statusTagTypeList: {
+        10: 'success',
+        20: 'info',
+        30: 'danger',
+      },
+    }
+  },
+  computed: {
+    ...mapGetters(['dict']),
+    dictList() {
+      return this.dict[dictEnum['客户/供应商资质类型']] || [];
+    },
+    typeList() {
+      return this.dict[dictEnum['工种类型']] || [];
+    },
+    columns() {
+      return [
+        {
+          type: 'index',
+          width: 55,
+          align: 'center'
+        },
+        {
+          label: '名称',
+          prop: 'name',
+          slot: 'name',
+          headerSlot: 'isRequired',
+          minWidth: 180,
+          align: 'center'
+        },
+        {
+          label: '编号',
+          prop: 'code',
+          slot: 'code',
+          headerSlot: 'isRequired',
+          minWidth: 120,
+          align: 'center'
+        },
+        {
+          label: '许可/经营范围',
+          prop: 'businessRange',
+          slot: 'businessRange',
+          minWidth: 140,
+          align: 'center'
+        },
+        {
+          label: '有效期起始日期',
+          prop: 'startTime',
+          slot: 'startTime',
+          headerSlot: 'isRequired',
+          minWidth: 160,
+          align: 'center'
+        },
+        {
+          label: '有效期截止日期',
+          prop: 'endTime',
+          slot: 'endTime',
+          headerSlot: 'isRequired',
+          minWidth: 160,
+          align: 'center'
+        },
+        {
+          label: '通知人',
+          prop: 'noticePersonName',
+          slot: 'noticePersonName',
+          headerSlot: 'isRequired',
+          minWidth: 140,
+          align: 'center'
+        },
+        {
+          label: '附件',
+          prop: 'accessory',
+          slot: 'accessory',
+          headerSlot: 'isRequired',
+          minWidth: 140,
+        },
+        {
+          label: '等级',
+          prop: 'level',
+          slot: 'level',
+          minWidth: 140,
+          align: 'center'
+        },
+        {
+          label: '分类',
+          prop: 'type',
+          slot: 'type',
+          minWidth: 140,
+          align: 'center'
+        },
+        {
+          label: '备注',
+          prop: 'remark',
+          slot: 'remark',
+          minWidth: 140,
+          align: 'center'
+        }
+      ]
+    },
+    disabledToType() {
+      return (row) => {
+        let list = this.form.detailsList.map(item => item.name)
+        let dictCodeList = this.dictList.map(item => item.dictCode)
+        let intersectionList = list.filter((v) => dictCodeList.indexOf(v) > -1)
+        intersectionList = intersectionList.filter(v => row.name !== v)
+        return intersectionList
+      }
+    },
+  },
+  created() {
+    this.requestDict('客户/供应商资质类型');
+    this.requestDict('工种类型');
+    this.getCertificateInfo({id:this.businessId})
+  },
+  methods: {
+    ...mapActions('dict', ['requestDict']),
+    getLabelName(arr, id) {
+      if (!id) return ''
+      return arr.find(item => item.dictCode == id)?.dictValue
+    },
+    //删除资质
+    handleRemove(index) {
+      this.form.detailsList.splice(index, 1)
+    },
+    //结束日期验证
+    validateEndDate(row, index) {
+      return (rule, value, callback) => {
+        if (!value) return callback(new Error(''))
+        if (
+          row.endTime &&
+          row.startTime &&
+          value < row.startTime
+        ) {
+          callback(new Error('截止日期不能小于起始日期'))
+        } else {
+          callback()
+        }
+      }
+    },
+    //页面初始化
+    init(type, row = {}) {
+      this.title = type == 'add' ? '新增' : type == 'edit' ? '修改' : '详情'
+      this.type = type
+      if (type !== 'add') {
+        this.getCertificateInfo(row)
+      }
+    },
+
+    handleClick() {
+      switch (this.form.certificationType) {
+        case '1':
+          this.$refs.clientSelection.open();
+          break;
+        case '2':
+          this.$refs.vendorDialogRef.open();
+          break;
+        case '3':
+          this.StaffType = 2
+          this.$refs.staffSelection.open([]);
+          break;
+      }
+    },
+    changeCertificationType(value) {
+      this.form.certificationType = value;
+      this.form.relationName = '';
+      this.form.relationId = '';
+    },
+    confirmSelection(obj) {
+      this.form.relationId = obj.id
+      this.form.relationName = obj.name
+      this.$forceUpdate();
+    },
+    async getCertificateInfo(row) {
+      this.form = await getProfessionCertificationById(row.id)
+      this.form.date = [this.form.startTime, this.form.endTime]
+    },
+    //打开选择负责人弹窗
+    openStaffSelection(index) {
+      this.curIndex = index
+      this.StaffType = 1
+      this.$refs.staffSelection.open([]);
+    },
+    //选择负责人回调
+    confirmStaffSelection(data, type) {
+      if (this.StaffType == 1) {
+        this.form.detailsList[this.curIndex].noticePersonName = data.map((item) => item.name).toString();
+        this.form.detailsList[this.curIndex].noticePersonId = data.map((item) => item.id).toString();
+      } else {
+        this.form.relationId = data.map((item) => item.id).join(',');
+        this.form.relationName = data.map((item) => item.name).join(',');
+      }
+
+    },
+    //新增
+    handleAdd() {
+      this.form.detailsList.push({...this.defaultData})
+    },
+    //修改资质证书
+    handleChangeType(val, row) {
+      if (!val) return row.name = ''
+      row.name = this.dictList.find(i => i.dictCode == val)?.dictValue || ''
+
+    },
+    downloadFile(file) {
+      getFile({objectName: file.storePath}, file.name);
+    },
+    //保存/提交
+    handleSave(isSub) {
+      this.$refs.form.validate(async (valid) => {
+        if (!valid) return this.$message.warning('有必填项未填写,请检查')
+        if (!this.form.detailsList.length) return this.$message.warning('至少保存一条资质信息')
+        this.form.startTime = this.form.date[0]
+        this.form.endTime = this.form.date[1]
+        let api = this.type == 'add' ? saveProfessionCertification : updateProfessionCertificationById
+        let id = await api(this.form)
+        if (isSub) {
+          let businessId = this.type == 'add' ? id : this.form.id
+          await contactQcSubmit({businessId: businessId, certificationType: this.form.certificationType})
+        }
+        this.$message.success('保存成功')
+        this.$emit('reload')
+        this.cancel()
+      })
+
+    },
+    //关闭弹窗
+    cancel() {
+      this.$emit('update:addOrEditDialogFlag', false)
+    },
+  }
+
+}
+</script>
+<style scoped lang="scss">
+:deep.el-form-item {
+  margin-bottom: 0
+}
+</style>

+ 181 - 0
src/views/bpm/handleTask/components/certificateQualifications/submit.vue

@@ -0,0 +1,181 @@
+<template>
+  <el-col :span="16" :offset="6">
+    <el-form label-width="100px" ref="formRef" :model="form">
+      <el-form-item
+        label="审批建议"
+        prop="reason"
+        style="margin-bottom: 20px"
+        :rules="{
+          required: true,
+          message: '请输入',
+          trigger: 'blur'
+        }"
+      >
+        <el-input
+          type="textarea"
+          v-model="form.reason"
+          placeholder="请输入审批建议"
+        />
+      </el-form-item>
+    </el-form>
+    <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
+      <el-button
+        icon="el-icon-edit-outline"
+        type="success"
+        size="mini"
+        @click="handleAudit(1)"
+      >通过
+      </el-button>
+      <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        @click="handleAudit(0)"
+        v-if="!['starter', 'starterFillApprove'].includes(taskDefinitionKey)"
+      >驳回
+      </el-button>
+
+      <el-dropdown @command="(command) => handleCommand(command)" style="margin-left: 30px;">
+        <span class="el-dropdown-link">更多<i class="el-icon-arrow-down el-icon--right"></i></span>
+        <el-dropdown-menu slot="dropdown">
+          <el-dropdown-item command="cancel">作废</el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+
+      <!-- <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        @click="handleBackList"
+        >退回
+      </el-button> -->
+      <!-- <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        @click="handleAudit(0)"
+        v-if="taskDefinitionKey != 'productionSupervisorApprove1'"
+        >不通过
+      </el-button>
+      <el-button
+        icon="el-icon-edit-outline"
+        type="primary"
+        size="mini"
+        v-if="taskDefinitionKey != 'productionSupervisorApprove1'"
+        @click="handleUpdateAssignee"
+        >转办
+      </el-button> -->
+    </div>
+  </el-col>
+</template>
+
+<script>
+import { cancel} from '@/api/bpm/components/supplierManage/contact';
+import {approveTaskWithVariables} from '@/api/bpm/task';
+import {listAllUserBind} from '@/api/system/organization';
+
+// 流程实例的详情页,可用于审批
+export default {
+  name: '',
+  components: {
+    //   Parser
+  },
+  props: {
+    businessId: {
+      default: ''
+    },
+    taskId: {
+      default: ''
+    },
+    id: {
+      default: ''
+    },
+    taskDefinitionKey: {
+      default: ''
+    }
+  },
+  data() {
+    return {
+      form: {
+        technicianId: '',
+        reason: ''
+      },
+      userOptions: []
+    };
+  },
+  created() {
+    this.userOptions = [];
+    listAllUserBind().then((data) => {
+      this.userOptions.push(...data);
+    });
+  },
+  methods: {
+    /** 处理转办审批人 */
+    handleUpdateAssignee() {
+      this.$emit('handleUpdateAssignee');
+    },
+    /** 退回 */
+    handleBackList() {
+      this.$emit('handleBackList');
+    },
+
+    async handleAudit(status) {
+      //发起人补充
+      // if (this.taskDefinitionKey === 'starter') {
+      //   await this.getTableValue();
+      // }
+      await this._approveTaskWithVariables(status);
+    },
+    async _approveTaskWithVariables(status) {
+      let variables = {
+        pass: !!status
+      };
+      approveTaskWithVariables({
+        id: this.taskId,
+        reason: this.form.reason,
+        variables
+      }).then((res) => {
+        if (res.data.code != '-1') {
+          this.$emit('handleAudit', {
+            status,
+            title: status === 0 ? '驳回' : ''
+          });
+        }
+      });
+    },
+
+    getTableValue() {
+      return new Promise((resolve, reject) => {
+        this.$emit('getTableValue', async (data) => {
+          resolve(await data);
+        });
+      });
+    },
+
+    //更多
+    handleCommand(command) {
+      if (command === 'cancel') {
+        this.$confirm("是否确认作废?", {
+          type: 'warning',
+          cancelButtonText: '取消',
+          confirmButtonText: '确定'
+        }).then(() => {
+          cancel({
+            id: this.taskId,
+            reason: this.form.reason,
+            businessId: this.businessId,
+          }).then(() => {
+            this.$emit('handleClose');
+          }).catch(() => {
+            this.$message.error("流程作废失败");
+          });
+        }).catch(() => {
+        });
+      }
+    },
+
+  }
+};
+</script>
+
+<style lang="scss"></style>

+ 53 - 17
src/views/home/index.vue

@@ -44,7 +44,7 @@
           <div class="card-cell-value">
             <div>
               <el-avatar shape="square" :size="80" src="https://empty" @error="errorHandler">
-                <img :src="xyy"/>
+                <img :src="user.info.avatarAddress"/>
               </el-avatar>
             </div>
             <div class="text-box">
@@ -145,8 +145,11 @@
         <div slot="header" class="clearfix">
           <span>常用功能</span>
         </div>
-        <div class="card-cell-content">
-          <div v-for="(item,index) in commonList" :key="item.id" class="card-cell-content-box" @click="handleAdd(item)">
+        <draggable v-model="commonList" class="card-cell-content">
+
+            <!--                   @click="handleCommonListDel(item,index)"></i>-->
+          <div v-for="(item,index) in commonList" :key="item.id" class="card-cell-content-box"
+               @click="handleAdd(item,index)" :title="!['-999','-1'].includes(item.id)&&isDelFlag?'点击删除':''" :class="!['-999','-1'].includes(item.id)&&isDelFlag?'div-del':''">
             <!--            <el-popover-->
             <!--              v-if="item.id !=='-1'"-->
             <!--              placement="top-start"-->
@@ -160,23 +163,23 @@
             <!--                <span>{{ item.name }}</span>-->
             <!--              </div>-->
             <!--            </el-popover>-->
-            <el-tooltip placement="top" effect="light" v-if="item.id !=='-1'">
-              <div slot="content">
-                <i style="color: red;cursor: pointer;font-size: 18px" class="el-icon-delete"
-                   @click="handleCommonListDel(item,index)"></i>
-              </div>
-              <div class="card-cell-content-div">
-                <i :class="item.icon||'el-icon-s-opportunity'"></i>
-                <span>{{ item.name }}</span>
-              </div>
-            </el-tooltip>
-            <div class="card-cell-content-div" v-else>
+            <!--            <el-tooltip placement="top" effect="light" v-if="item.id !=='-1'">-->
+            <!--              <div slot="content">-->
+            <!--                <i style="color: red;cursor: pointer;font-size: 18px" class="el-icon-delete"-->
+            <!--                   @click="handleCommonListDel(item,index)"></i>-->
+            <!--              </div>-->
+            <!--              <div class="card-cell-content-div">-->
+            <!--                <i :class="item.icon||'el-icon-s-opportunity'"></i>-->
+            <!--                <span>{{ item.name }}</span>-->
+            <!--              </div>-->
+            <!--            </el-tooltip>-->
+            <div class="card-cell-content-div">
               <i :class="item.icon||'el-icon-s-opportunity'"></i>
               <span>{{ item.name }}</span>
             </div>
           </div>
 
-        </div>
+        </draggable>
       </el-card>
     </div>
     <handleTask ref="handleTaskRef" @reload="reload"></handleTask>
@@ -200,9 +203,11 @@ import {getWorkOrderPage} from "@/api/tickets";
 import {statistics} from "@/api/bpm/components/inspectionManage";
 import commonDialog from "@/views/home/common-dialog.vue";
 import xyy from '@/assets/xyy.jpg'
+import draggable from 'vuedraggable';
+
 export default {
   name: "index",
-  components: {handleTask, detail, vueSeamlessScroll, commonDialog},
+  components: {handleTask, detail, vueSeamlessScroll, commonDialog, draggable},
   data() {
     return {
       xyy,
@@ -226,6 +231,7 @@ export default {
         waitTime: 1000 // 单步运动停止的时间(默认值1000ms)
       },
       commonList: [],
+      isDelFlag: false,
       columns: [
         {
           columnKey: 'index',
@@ -323,18 +329,33 @@ export default {
         id: '-1',
         icon: 'el-icon-edit',
       })
+      this.commonList.push({
+        name: '删除',
+        id: '-999',
+        icon: 'el-icon-delete',
+      })
     },
     handelRouterTo(path) {
       window.history.pushState(null, '', path)
       // this.$router.push(path);
     },
-    handleAdd(item) {
+    handleAdd(item, index) {
       if (item.id == -1) {
         this.commonDialogFlag = true
         this.$nextTick(() => {
           this.$refs.commonDialogRef.init()
         })
+      } else if (item.id == -999) {
+        this.isDelFlag = !this.isDelFlag
+        this.commonList.pop()
+        this.commonList.push({
+          name: `${this.isDelFlag?'取消删除':'删除'}`,
+          id: '-999',
+          icon: 'el-icon-delete',
+        })
       } else {
+        if (this.isDelFlag) return this.handleCommonListDel(item, index)
+
         let urlPath = item.topUrl + item.url
         this.handelRouterTo(urlPath)
       }
@@ -709,4 +730,19 @@ export default {
   padding: 0;
 }
 
+
+.div-del {
+  background: #cccccc !important;
+  position: relative;
+}
+.div-del::after {
+  content: "—";
+  position: absolute;
+  padding: 5px; /* 按钮的内边距 */
+  background-color: rgba(255, 0, 0, 0.91);
+  color: white;
+  border-radius: 50%;
+
+}
+
 </style>

+ 6 - 2
src/views/login/index.vue

@@ -70,13 +70,15 @@
 import I18nIcon from '@/layout/components/i18n-icon.vue';
 import { getToken } from '@/utils/token-util';
 import { login, getCaptcha, sub } from '@/api/login';
-
+import {getPathAddress} from "@/api/system/file";
+import  xyy from '@/assets/xyy.jpg'
 export default {
   // eslint-disable-next-line vue/multi-word-component-names
   name: 'Login',
   components: { I18nIcon },
   data() {
     return {
+      xyy,
       // 登录框方向, 0居中, 1居右, 2居左
       direction: 0,
       // 加载状态
@@ -133,9 +135,11 @@ export default {
         }
         this.loading = true;
         login(this.form)
-          .then((res) => {
+          .then(async (res) => {
             localStorage.setItem('userId', res.data.userId);
             // 用户信息
+            const filePath = await getPathAddress()
+            res.data.avatarAddress = res.data.avatar && res.data.avatar.length ? filePath + res.data.avatar[0].storePath : xyy
             this.$store.commit('user/setUserInfo', res.data);
             this.loading = false;
             this.$message.success(res.message);

+ 472 - 0
src/views/user/profile/index.vue

@@ -0,0 +1,472 @@
+<template>
+  <div class="ele-body">
+    <el-row :gutter="15">
+      <el-col v-bind="styleResponsive ? { md: 8, sm: 8 } : { span: 8 }">
+        <el-card shadow="never" body-style="padding: 25px;">
+          <div class="user-info-card">
+            <div class="user-info-avatar-group" @click="openCropper">
+              <img class="user-info-avatar" :src="form.img" crossOrigin="anonymous" alt=""/>
+              <i class="el-icon-upload2"></i>
+            </div>
+          </div>
+          <ul class="list-group list-group-striped">
+            <li class="list-group-item">
+              <i class="el-icon-user"/>
+              用户名称
+              <div class="pull-right">{{ this.form.name }}</div>
+            </li>
+            <li class="list-group-item">
+              <i class="el-icon-mobile"/>
+              手机号码
+              <div class="pull-right">{{ this.form.phone }}</div>
+            </li>
+            <li class="list-group-item">
+              <i class="el-icon-message"/>
+              用户邮箱
+              <div class="pull-right">{{ this.form.email }}</div>
+            </li>
+            <li class="list-group-item">
+              <i class="el-icon-s-operation"/>
+              所属部门
+              <div class="pull-right">{{ this.form.deptName }}</div>
+            </li>
+            <li class="list-group-item">
+              <i class="el-icon-suitcase"/>
+              所属岗位
+              <div class="pull-right">{{ this.form.postName }}</div>
+            </li>
+            <!--            <li class="list-group-item">-->
+            <!--              <i class="el-icon-s-custom"/>-->
+            <!--              所属角色-->
+            <!--              <div class="pull-right" v-if="user.roles">{{ user.roles.map(role => role.name).join(',') }}</div>-->
+            <!--            </li>-->
+            <li class="list-group-item">
+              <i class="el-icon-date"/>
+              创建日期
+              <div class="pull-right">{{ this.form.createTime }}</div>
+            </li>
+          </ul>
+          <div style="margin: 30px 0 20px 0">
+            <el-divider class="ele-divider-dashed ele-divider-base"/>
+          </div>
+        </el-card>
+      </el-col>
+      <el-col v-bind="styleResponsive ? { md: 16, sm: 16 } : { span: 16 }">
+        <el-card shadow="never" body-style="padding: 0;">
+          <el-tabs v-model="active" class="user-info-tabs">
+            <el-tab-pane label="基本信息" name="info">
+              <el-form
+                ref="form"
+                :model="form"
+                :rules="rules"
+                label-width="90px"
+                style="max-width: 450px; padding: 34px 20px 0 20px"
+                @keyup.enter.native="save"
+                @submit.native.prevent
+              >
+                <el-form-item label="用户名称:" prop="loginName">
+                  <el-input
+                    v-model="form.name"
+                    placeholder="请输入昵称"
+                    clearable
+                  />
+                </el-form-item>
+                <el-form-item label="性别:" prop="sex">
+                  <el-select
+                    v-model="form.sex"
+                    placeholder="请选择性别"
+                    class="ele-fluid"
+                    clearable
+                  >
+                    <el-option label="男" :value="1"/>
+                    <el-option label="女" :value="2"/>
+                  </el-select>
+                </el-form-item>
+                <el-form-item label="邮箱:" prop="email">
+                  <el-input
+                    v-model="form.email"
+                    placeholder="请输入邮箱"
+                    clearable
+                  />
+                </el-form-item>
+                <el-form-item label="联系电话:">
+                  <div class="ele-cell">
+                    <div class="ele-cell-content">
+                      <el-input
+                        v-model="form.phone"
+                        placeholder="请输入联系电话"
+                        clearable
+                      />
+                    </div>
+                  </div>
+                </el-form-item>
+                <el-form-item>
+                  <el-button type="primary" :loading="loading" @click="save">
+                    保存更改
+                  </el-button>
+                </el-form-item>
+              </el-form>
+            </el-tab-pane>
+            <!--            <el-tab-pane label="账号绑定" name="account">-->
+            <!--              <div class="user-account-list">-->
+            <!--                <div class="user-account-item ele-cell">-->
+            <!--                  <div class="ele-cell-content">-->
+            <!--                    <div>密保手机</div>-->
+            <!--                    <div class="ele-text-secondary">-->
+            <!--                      已绑定手机: 138****8293-->
+            <!--                    </div>-->
+            <!--                  </div>-->
+            <!--                  <el-link type="primary" :underline="false">去修改</el-link>-->
+            <!--                </div>-->
+            <!--                <el-divider/>-->
+            <!--                <div class="user-account-item ele-cell">-->
+            <!--                  <div class="ele-cell-content">-->
+            <!--                    <div>密保邮箱</div>-->
+            <!--                    <div class="ele-text-secondary">-->
+            <!--                      已绑定邮箱: eleadmin@eclouds.com-->
+            <!--                    </div>-->
+            <!--                  </div>-->
+            <!--                  <el-link type="primary" :underline="false">去修改</el-link>-->
+            <!--                </div>-->
+            <!--                <el-divider/>-->
+            <!--                <div class="user-account-item ele-cell">-->
+            <!--                  <div class="ele-cell-content">-->
+            <!--                    <div>密保问题</div>-->
+            <!--                    <div class="ele-text-secondary">未设置密保问题</div>-->
+            <!--                  </div>-->
+            <!--                  <el-link type="primary" :underline="false">去设置</el-link>-->
+            <!--                </div>-->
+            <!--                <el-divider/>-->
+            <!--                <div class="user-account-item ele-cell">-->
+            <!--                  <i class="user-account-icon el-icon-_qq"></i>-->
+            <!--                  <div class="ele-cell-content">-->
+            <!--                    <div>绑定QQ</div>-->
+            <!--                    <div class="ele-text-secondary">当前未绑定QQ账号</div>-->
+            <!--                  </div>-->
+            <!--                  <el-link type="primary" :underline="false">去绑定</el-link>-->
+            <!--                </div>-->
+            <!--                <el-divider/>-->
+            <!--                <div class="user-account-item ele-cell">-->
+            <!--                  <i class="user-account-icon el-icon-_wechat"></i>-->
+            <!--                  <div class="ele-cell-content">-->
+            <!--                    <div>绑定微信</div>-->
+            <!--                    <div class="ele-text-secondary">当前未绑定绑定微信账号</div>-->
+            <!--                  </div>-->
+            <!--                  <el-link type="primary" :underline="false">去绑定</el-link>-->
+            <!--                </div>-->
+            <!--                <el-divider/>-->
+            <!--                <div class="user-account-item ele-cell">-->
+            <!--                  <i class="user-account-icon el-icon-_alipay"></i>-->
+            <!--                  <div class="ele-cell-content">-->
+            <!--                    <div>绑定支付宝</div>-->
+            <!--                    <div class="ele-text-secondary">-->
+            <!--                      当前未绑定绑定支付宝账号-->
+            <!--                    </div>-->
+            <!--                  </div>-->
+            <!--                  <el-link type="primary" :underline="false">去绑定</el-link>-->
+            <!--                </div>-->
+            <!--              </div>-->
+            <!--            </el-tab-pane>-->
+          </el-tabs>
+        </el-card>
+      </el-col>
+    </el-row>
+    <!-- 头像裁剪弹窗 -->
+    <ele-cropper-dialog
+      :src="form.img"
+      :show.sync="visible"
+      :lock-scroll="false"
+      :destroy-on-close="true"
+      :close-on-click-modal="false"
+      :options="{ autoCropArea: 1, viewMode: 1, dragMode: 'move' }"
+      @crop="onCrop"
+    />
+  </div>
+</template>
+
+<script>
+import EleCropperDialog from 'ele-admin/es/ele-cropper-dialog';
+import {mapGetters} from "vuex";
+import {getUserDetail, saveOrUpdateUser} from "@/api/system/organization";
+import xyy from '@/assets/xyy.jpg'
+import {emailReg} from "ele-admin";
+import {getPathAddress, uploadFile} from "@/api/system/file";
+import {getImageUrl} from "@/utils/file";
+
+export default {
+  name: 'UserProfile',
+  components: {EleCropperDialog},
+  data() {
+    return {
+      xyy,
+      // tab页选中
+      active: 'info',
+      // 表单数据
+      form: {
+        name: '',
+        sex: '',
+        email: '',
+        phone: '',
+        img: '',
+        avatar: []
+      },
+      // 表单验证规则
+      rules: {
+        nickname: [
+          {
+            required: true,
+            message: '请输入昵称',
+            trigger: 'blur'
+          }
+        ],
+        sex: [
+          {
+            required: true,
+            message: '请选择性别',
+            trigger: 'blur'
+          }
+        ],
+        email: [
+
+          {pattern: emailReg, message: '邮箱格式不正确', trigger: 'blur'}
+        ]
+      },
+      // 保存按钮loading
+      loading: false,
+      // 是否显示裁剪弹窗
+      visible: false,
+      userInfo: {}
+    };
+  },
+  computed: {
+    // 登录用户信息
+    ...mapGetters(['user']),
+    // 是否开启响应式布局
+    styleResponsive() {
+      return this.$store.state.theme.styleResponsive;
+    }
+  },
+  async created() {
+    this.userInfo = await getUserDetail(this.user.info.userId)
+    Object.assign(this.form, this.userInfo);
+    const filePath = await getPathAddress()
+    this.form.img = this.userInfo.avatar ? filePath + this.userInfo.avatar[0].storePath : xyy
+  },
+  methods: {
+    /* 保存更改 */
+    save() {
+      this.$refs.form.validate(async (valid) => {
+        if (!valid) {
+          return false;
+        }
+        this.loading = true;
+        await saveOrUpdateUser(this.form);
+        this.loading = false;
+        this.$message.success('保存成功');
+      });
+    },
+    // /* 修改登录用户 */
+    // updateLoginUser(data) {
+    //   this.$store.dispatch('user/setInfo', {...this.loginUser, ...data});
+    // },
+    /* 头像裁剪完成回调 */
+    async onCrop(result) {
+      // this.form.avatar = result;
+      this.visible = false;
+      let file = this.base64ToFile(result, '头像.jpg')
+      file.uid = 2222
+      const {data} = await uploadFile(
+        {
+          module: 'main',
+          multiPartFile: file
+        }
+      )
+      this.form.avatar = [data]
+      const filePath = await getPathAddress()
+      this.form.img = filePath + data.storePath
+      await saveOrUpdateUser(this.form);
+      window.setGlobalState.commit('user/setUserInfo', {
+        ...this.user.info,
+        avatar: this.form.avatar,
+        avatarAddress: this.form.img
+      });
+      console.log(this.user,'======');
+    },
+    /* 打开图片裁剪 */
+    openCropper() {
+      this.visible = true;
+    },
+    base64ToFile(base64, fileName) {
+      let arr = base64.split(',')
+      let type = arr[0].match(/:(.*?);/)[1]
+      let bstr = atob(arr[1])
+      let n = bstr.length
+      let u8arr = new Uint8Array(n)
+      while (n--) {
+        u8arr[n] = bstr.charCodeAt(n)
+      }
+      return new File([u8arr], fileName, {type})
+    }
+  },
+
+
+};
+</script>
+
+<style lang="scss" scoped>
+.ele-body {
+  padding-bottom: 0;
+}
+
+.el-card {
+  margin-bottom: 15px;
+}
+
+/* 用户资料卡片 */
+.user-info-card {
+  padding: 8px 0;
+  text-align: center;
+
+  .user-info-avatar-group {
+    position: relative;
+    cursor: pointer;
+    margin: 0 auto;
+    width: 110px;
+    height: 110px;
+    border-radius: 50%;
+    overflow: hidden;
+
+    & > i {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      transform: translate(-50%, -50%);
+      color: #fff;
+      font-size: 30px;
+      display: none;
+      z-index: 2;
+    }
+
+    &:hover {
+      & > i {
+        display: block;
+      }
+
+      &:after {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        background-color: rgba(0, 0, 0, 0.3);
+      }
+    }
+  }
+
+  .user-info-avatar {
+    width: 110px;
+    height: 110px;
+    border-radius: 50%;
+    object-fit: cover;
+  }
+
+  .user-info-name {
+    font-size: 24px;
+    margin-top: 20px;
+  }
+
+  .user-info-desc {
+    margin-top: 8px;
+  }
+}
+
+/* 用户信息列表 */
+.user-info-list {
+  margin-top: 30px;
+
+  .user-info-item {
+    margin-bottom: 16px;
+    display: flex;
+    align-items: baseline;
+
+    & > i {
+      margin-right: 10px;
+      font-size: 16px;
+    }
+
+    & > span {
+      flex: 1;
+      display: block;
+    }
+  }
+}
+
+/* 用户标签 */
+.user-info-tags .el-tag {
+  margin: 10px 10px 0 0;
+}
+
+/* 用户账号绑定列表 */
+.user-account-list {
+  padding: 16px 20px;
+
+  .user-account-item {
+    padding: 15px;
+
+    .ele-text-secondary {
+      margin-top: 6px;
+    }
+
+    .user-account-icon {
+      width: 42px;
+      height: 42px;
+      line-height: 42px;
+      text-align: center;
+      color: #fff;
+      font-size: 26px;
+      border-radius: 50%;
+      background-color: #3492ed;
+      box-sizing: border-box;
+
+      &.el-icon-_wechat {
+        background-color: #4daf29;
+        font-size: 28px;
+      }
+
+      &.el-icon-_alipay {
+        background-color: #1476fe;
+        padding-left: 5px;
+        font-size: 32px;
+      }
+    }
+  }
+}
+
+/* tab 页签 */
+.user-info-tabs {
+  :deep(.el-tabs__nav-wrap) {
+    padding-left: 24px;
+  }
+
+  :deep(.el-tabs__item) {
+    height: 50px;
+    line-height: 50px;
+    font-size: 15px;
+  }
+}
+
+.list-group {
+  padding-left: 0;
+  list-style: none;
+}
+
+.list-group-item {
+
+  padding: 11px 0;
+  margin-bottom: -1px;
+  font-size: 13px;
+  border-top: 1px solid #e7eaec;
+  border-bottom: 1px solid #e7eaec;
+}
+</style>