huangxw 7 miesięcy temu
rodzic
commit
9d2f46d3ea

+ 62 - 0
src/api/authority/index.ts

@@ -0,0 +1,62 @@
+import request, { download, downloadFile } from '@/utils/request';
+import { AxiosPromise } from 'axios';
+// 查询专家信息列表
+export const expertList = (query?: any): AxiosPromise => {
+    return request({
+        url: '/dgtmedicine/expertPerson/list',
+        method: 'get',
+        params: query
+    });
+};
+// 新增专家信息
+export const expertPersonAdd = (data: any): AxiosPromise => {
+    return request({
+        url: '/dgtmedicine/expertPerson/addPerson',
+        method: 'post',
+        data
+    });
+};
+// 修改专家信息
+export const expertPersonUpdate = (data: any): AxiosPromise => {
+    return request({
+        url: '/dgtmedicine/expertPerson/editPerson',
+        method: 'post',
+        data
+    });
+};
+// 下架专家信息
+export const expertPersonOff = (id: any): AxiosPromise => {
+    return request({
+        url: `/dgtmedicine/expertPerson/unpublish/${id}`,
+        method: 'get'
+    });
+};
+// 上架专家信息
+export const expertPersonOn = (id: any): AxiosPromise => {
+    return request({
+        url: `/dgtmedicine/expertPerson/publish/${id}`,
+        method: 'get'
+    });
+};
+// 获取专家信息详细信息
+export const expertPersonDetail = (id: any): AxiosPromise => {
+    return request({
+        url: `/dgtmedicine/expertPerson/getInfo/${id}`,
+        method: 'get'
+    });
+};
+// 删除专家信息
+export const expertPersonDelete = (id: any): AxiosPromise => {
+    return request({
+        url: `/dgtmedicine/expertPerson/delPerson/${id}`,
+        method: 'get'
+    });
+};
+// 专家统计
+export const expertPersonCount = (query?: any): AxiosPromise => {
+    return request({
+        url: '/dgtmedicine/expertPerson/queryPersonCount',
+        method: 'get',
+        params: query
+    });
+};

+ 21 - 13
src/api/system/oss/index.ts

@@ -4,25 +4,33 @@ import { AxiosPromise } from 'axios';
 
 // 查询OSS对象存储列表
 export function listOss(query: OssQuery): AxiosPromise<OssVO[]> {
-  return request({
-    url: '/resource/oss/list',
-    method: 'get',
-    params: query
-  });
+    return request({
+        url: '/resource/oss/list',
+        method: 'get',
+        params: query
+    });
 }
 
 // 查询OSS对象基于id串
 export function listByIds(ossId: string | number): AxiosPromise<OssVO[]> {
-  return request({
-    url: '/resource/oss/listByIds/' + ossId,
-    method: 'get'
-  });
+    return request({
+        url: '/resource/oss/listByIds/' + ossId,
+        method: 'get'
+    });
 }
 
 // 删除OSS对象存储
 export function delOss(ossId: string | number | Array<string | number>) {
-  return request({
-    url: '/resource/oss/' + ossId,
-    method: 'delete'
-  });
+    return request({
+        url: '/resource/oss/' + ossId,
+        method: 'delete'
+    });
+}
+// 上传
+export function uploadFile(data: FormData) {
+    return request({
+        url: '/resource/oss/upload',
+        method: 'post',
+        data
+    });
 }

BIN
src/assets/images/avater.jpg


+ 0 - 11
src/assets/styles/ruoyi.scss

@@ -300,17 +300,6 @@ h6 {
   height: 120px;
 }
 
-.avatar-upload-preview {
-  position: absolute;
-  top: 50%;
-  transform: translate(50%, -50%);
-  width: 200px;
-  height: 200px;
-  border-radius: 50%;
-  box-shadow: 0 0 4px #ccc;
-  overflow: hidden;
-}
-
 /* 拖拽列样式 */
 .sortable-ghost {
   opacity: 0.8;

+ 55 - 60
src/components/DictTag/index.vue

@@ -1,21 +1,16 @@
 <template>
-  <div>
-    <template v-for="(item, index) in options">
-      <template v-if="values.includes(item.value)">
-        <span
-          v-if="(item.elTagType === 'default' || item.elTagType === '') && (item.elTagClass === '' || item.elTagClass == null)"
-          :key="item.value"
-          :index="index"
-          :class="item.elTagClass"
-        >
-          {{ item.label + ' ' }}
-        </span>
-        <el-tag
-          v-else
-          :key="item.value + ''"
-          :disable-transitions="true"
-          :index="index"
-          :type="
+    <div>
+        <template v-for="(item, index) in options">
+            <template v-if="values.includes(item.value)">
+                <span v-if="(item.elTagType === 'default' || item.elTagType === '') && (item.elTagClass === '' || item.elTagClass == null)" :key="item.value" :index="index" :class="item.elTagClass">
+                    {{ item.label + ' ' }}
+                </span>
+                <el-tag
+                    v-else
+                    :key="item.value + ''"
+                    :disable-transitions="true"
+                    :index="index"
+                    :type="
             item.elTagType === 'primary' ||
             item.elTagType === 'success' ||
             item.elTagType === 'info' ||
@@ -24,71 +19,71 @@
               ? item.elTagType
               : 'primary'
           "
-          :class="item.elTagClass"
-        >
-          {{ item.label + ' ' }}
-        </el-tag>
-      </template>
-    </template>
-    <template v-if="unmatch && showValue">
-      {{ unmatchArray }}
-    </template>
-  </div>
+                    :class="item.elTagClass"
+                >
+                    {{ item.label + ' ' }}
+                </el-tag>
+            </template>
+        </template>
+        <template v-if="unmatch && showValue">
+            {{ unmatchArray }}
+        </template>
+    </div>
 </template>
 
 <script setup lang="ts">
 interface Props {
-  options: Array<DictDataOption>;
-  value: number | string | Array<number | string>;
-  showValue?: boolean;
-  separator?: string;
+    options: Array<DictDataOption>;
+    value: number | string | Array<number | string>;
+    showValue?: boolean;
+    separator?: string;
 }
 const props = withDefaults(defineProps<Props>(), {
-  showValue: true,
-  separator: ','
+    showValue: true,
+    separator: ','
 });
 
 const values = computed(() => {
-  if (props.value === '' || props.value === null || typeof props.value === 'undefined') return [];
-  return Array.isArray(props.value) ? props.value.map((item) => '' + item) : String(props.value).split(props.separator);
+    if (props.value === '' || props.value === null || typeof props.value === 'undefined') return [];
+    return Array.isArray(props.value) ? props.value.map((item) => '' + item) : String(props.value).split(props.separator);
 });
 
 const unmatch = computed(() => {
-  if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === 'undefined') return false;
-  // 传入值为非数组
-  let unmatch = false; // 添加一个标志来判断是否有未匹配项
-  values.value.forEach((item) => {
-    if (!props.options.some((v) => v.value === item)) {
-      unmatch = true; // 如果有未匹配项,将标志设置为true
-    }
-  });
-  return unmatch; // 返回标志的值
+    if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === 'undefined') return false;
+    // 传入值为非数组
+    let unmatch = false; // 添加一个标志来判断是否有未匹配项
+    values.value.forEach((item) => {
+        if (!props.options.some((v) => v.value === item)) {
+            unmatch = true; // 如果有未匹配项,将标志设置为true
+        }
+    });
+    return unmatch; // 返回标志的值
 });
 
 const unmatchArray = computed(() => {
-  // 记录未匹配的项
-  const itemUnmatchArray: Array<string | number> = [];
-  if (props.value !== '' && props.value !== null && typeof props.value !== 'undefined') {
-    values.value.forEach((item) => {
-      if (!props.options.some((v) => v.value === item)) {
-        itemUnmatchArray.push(item);
-      }
-    });
-  }
-  // 没有value不显示
-  return handleArray(itemUnmatchArray);
+    // 记录未匹配的项
+    const itemUnmatchArray: Array<string | number> = [];
+    if (props.value !== '' && props.value !== null && typeof props.value !== 'undefined') {
+        values.value.forEach((item) => {
+            if (!props.options.some((v) => v.value === item)) {
+                itemUnmatchArray.push(item);
+            }
+        });
+    }
+    // 没有value不显示
+    return handleArray(itemUnmatchArray);
 });
 
 const handleArray = (array: Array<string | number>) => {
-  if (array.length === 0) return '';
-  return array.reduce((pre, cur) => {
-    return pre + ' ' + cur;
-  });
+    if (array.length === 0) return '';
+    return array.reduce((pre, cur) => {
+        return pre + ' ' + cur;
+    });
 };
 </script>
 
 <style scoped>
 .el-tag + .el-tag {
-  margin-left: 10px;
+    margin-left: 10px;
 }
 </style>

+ 38 - 0
src/components/HAvatar/HAvatar.vue

@@ -0,0 +1,38 @@
+<template>
+    <el-avatar class="head-avater" @click="showImg = true" :size="size" :src="src" @error="errorHandler" :fit="fit" :shape="shape">
+        <div v-if="name" :style="{ width: size + 'px', height: size + 'px', fontSize: size / 2 + 'px' }" class="text-head d-flex a-c j-c f-14">
+            {{ name.slice(0, 1) }}
+        </div>
+        <img v-else :src="defSrc || avaterDef" />
+    </el-avatar>
+    <ImgViewer v-if="isView && src" v-model:show="showImg" :imgs="[src]"></ImgViewer>
+</template>
+
+<script setup lang="ts" name="HAvatar">
+import { propTypes } from '@/utils/propTypes';
+import avaterDef from '@/assets/images/avater.jpg'
+const errorHandler = () => true
+defineProps({
+    src: propTypes.string.def(''),
+    name: propTypes.string.def(''),
+    size: propTypes.number.def(40),
+    defSrc: propTypes.string.def(''),
+    fit: propTypes.any.def('cover'),
+    shape: propTypes.any.def('circle'), // square
+    isView: propTypes.bool.def(false)
+});
+const showImg = ref(false)
+</script>
+
+<style scoped lang="scss">
+.text-head {
+    background-color: var(--el-color-primary);
+}
+</style>
+<style lang="scss">
+.head-avater {
+    &.el-avatar {
+        background-color: transparent;
+    }
+}
+</style>

+ 257 - 0
src/components/UploadAvatar/UploadAvatar.vue

@@ -0,0 +1,257 @@
+<template>
+    <div class="head-img p-rtv c-s-p" @click="editCropper()">
+        <slot>
+            <HAvatar :size="82" :src="(options.img as string)" :name="options.name" :defSrc="defSrc" :fit="fit" :shape="shape"></HAvatar>
+            <div v-if="isIcon" class="icon-edit">
+                <el-icon><EditPen /></el-icon>
+            </div>
+        </slot>
+        <el-dialog :title="title" v-model="open" width="800px" custom-class="custom-dialog" append-to-body @opened="modalOpened" @close="closeDialog">
+            <el-row>
+                <el-col :xs="24" :md="12" :style="{ height: '350px' }">
+                    <vue-cropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop" :maxImgSize="4000" :fixedBox="options.fixedBox" :autoCropWidth="300" :autoCropHeight="300" :full="true" :outputType="options.outputType" @realTime="realTime" v-if="visible" />
+                </el-col>
+                <el-col :xs="24" :md="12" class="d-flex j-c a-c" :style="{ height: '350px' }">
+                    <div class="avatar-upload-preview" :class="{ 'square':  shapeImg === 'square'}">
+                        <img :src="options.previews.url" :style="options.previews.img" />
+                    </div>
+                </el-col>
+            </el-row>
+            <br />
+            <el-row>
+                <el-col :lg="2" :md="2">
+                    <el-upload action="#" :http-request="requestUpload" :show-file-list="false" accept=".png,.jpg,.jpeg,.bmp" :before-upload="beforeUpload">
+                        <el-button>
+                            选择
+                            <el-icon class="el-icon--right">
+                                <Upload />
+                            </el-icon>
+                        </el-button>
+                    </el-upload>
+                </el-col>
+                <el-col :lg="{ span: 1, offset: 2 }" :md="2">
+                    <el-button icon="Plus" @click="changeScale(1)"></el-button>
+                </el-col>
+                <el-col :lg="{ span: 1, offset: 1 }" :md="2">
+                    <el-button icon="Minus" @click="changeScale(-1)"></el-button>
+                </el-col>
+                <el-col :lg="{ span: 1, offset: 1 }" :md="2">
+                    <el-button icon="RefreshLeft" @click="rotateLeft()"></el-button>
+                </el-col>
+                <el-col :lg="{ span: 1, offset: 1 }" :md="2">
+                    <el-button icon="RefreshRight" @click="rotateRight()"></el-button>
+                </el-col>
+                <el-col :lg="{ span: 2, offset: 6 }" :md="2">
+                    <el-button type="primary" @click="uploadImg()">提 交</el-button>
+                </el-col>
+            </el-row>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup lang="ts">
+import 'vue-cropper/dist/index.css';
+import { VueCropper } from 'vue-cropper';
+import useUserStore from '@/store/modules/user';
+import { propTypes } from '@/utils/propTypes';
+import { uploadFile } from '@/api/system/oss';
+
+interface Options {
+    img: string | ArrayBuffer | null; // 裁剪图片的地址
+    autoCrop: boolean; // 是否默认生成截图框
+    name: string; // 是否默认生成截图框
+    // autoCropWidth: number; // 默认生成截图框宽度
+    // autoCropHeight: number; // 默认生成截图框高度
+    fixedBox: boolean; // 固定截图框大小 不允许改变
+    fileName: string;
+    previews: any; // 预览数据
+    outputType: string;
+    visible: boolean;
+}
+const props = defineProps({
+    title: propTypes.string.def('修改头像'),
+    modelValue: propTypes.any.def(null),
+    name: propTypes.string.def(''),
+    defSrc: propTypes.string.def(''),
+    isIcon: propTypes.bool.def(true),
+    fit: propTypes.any.def('cover'),
+    shape: propTypes.any.def('circle'),
+    isObject: propTypes.bool.def(false),
+    shapeImg: propTypes.any.def('circle')
+});
+
+const userStore = useUserStore();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const open = ref(false);
+const visible = ref(false);
+const cropper = ref<any>({});
+//图片裁剪数据
+const options = reactive<Options>({
+    img: '',
+    name: '',
+    autoCrop: true,
+    fixedBox: true,
+    outputType: 'jpeg',
+    fileName: '',
+    previews: {},
+    visible: false
+});
+
+/** 编辑头像 */
+const editCropper = () => {
+    open.value = true;
+};
+/** 打开弹出层结束时的回调 */
+const modalOpened = () => {
+    visible.value = true;
+};
+/** 覆盖默认上传行为 */
+const requestUpload = (): any => {};
+/** 向左旋转 */
+const rotateLeft = () => {
+    cropper.value.rotateLeft();
+};
+/** 向右旋转 */
+const rotateRight = () => {
+    cropper.value.rotateRight();
+};
+/** 图片缩放 */
+const changeScale = (num: number) => {
+    num = num || 1;
+    cropper.value.changeScale(num);
+};
+/** 上传预处理 */
+const beforeUpload = (file: any) => {
+    if (file.type.indexOf('image/') == -1) {
+        proxy?.$modal.msgError('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件。');
+    } else {
+        const reader = new FileReader();
+        reader.readAsDataURL(file);
+        reader.onload = () => {
+            options.img = reader.result;
+            // 获取图片后缀名
+            const suffix = file.name.split('.').pop();
+            if (suffix === 'png') {
+                options.outputType = 'png';
+            } else {
+                options.outputType = 'jpeg';
+            }
+            options.fileName = file.name;
+        };
+    }
+};
+const emit = defineEmits(['update:modelValue', 'change']);
+/** 上传图片 */
+const uploadImg = async () => {
+    cropper.value.getCropBlob(async (data: any) => {
+        let formData = new FormData();
+        formData.append('file', data, options.fileName);
+        const res = await uploadFile(formData);
+        if (!res || res.code !== 200) return;
+        if (props.isObject) {
+            emit('update:modelValue', { url: res.data.url, fileName: options.fileName });
+            emit('change', { url: res.data.url, fileName: options.fileName });
+        } else {
+            emit('update:modelValue', res.data.url);
+            emit('change', res.data.url);
+        }
+
+        open.value = false;
+    });
+};
+/** 实时预览 */
+const realTime = (data: any) => {
+    options.previews = data;
+};
+/** 关闭窗口 */
+const closeDialog = () => {
+    options.visible = false;
+};
+watch(
+    () => props.modelValue,
+    async (val) => {
+        if (val) {
+            if (props.isObject) {
+                options.img = val?.url as string;
+                options.fileName = val?.fileName as string;
+                // 获取图片后缀名
+                const suffix = val?.url?.split('.').pop();
+                if (suffix === 'png') {
+                    options.outputType = 'png';
+                } else {
+                    options.outputType = 'jpeg';
+                }
+            } else {
+                options.img = val as string;
+                const suffix = val?.split('.').pop();
+                if (suffix === 'png') {
+                    options.outputType = 'png';
+                } else {
+                    options.outputType = 'jpeg';
+                }
+            }
+        }
+    },
+    { deep: true, immediate: true }
+);
+watch(
+    () => props.name,
+    async (val) => {
+        if (val) {
+            options.name = val as string;
+        }
+    },
+    { deep: true, immediate: true }
+);
+</script>
+
+<style lang="scss" scoped>
+.user-info-head {
+    position: relative;
+    display: inline-block;
+    height: 120px;
+}
+
+.user-info-head:hover:after {
+    content: '+';
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    color: #eee;
+    background: rgba(0, 0, 0, 0.5);
+    font-size: 24px;
+    font-style: normal;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+    cursor: pointer;
+    line-height: 110px;
+    border-radius: 50%;
+}
+.icon-edit {
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    width: 20px;
+    height: 20px;
+    color: #fff;
+    line-height: 20px;
+    text-align: center;
+    font-size: 14px;
+    background: radial-gradient(70% 0% at 0% 0%, #0fd2a8 0%, #01e093 100%), #ffffff;
+    border-radius: 50%;
+}
+.avatar-upload-preview.square {
+    border-radius: 2px;
+}
+.avatar-upload-preview {
+    width: 300px;
+    height: 300px;
+    border-radius: 50%;
+    margin: auto;
+    box-shadow: 0 0 4px #ccc;
+    overflow: hidden;
+}
+</style>

+ 217 - 0
src/views/authority/authority-input/index.vue

@@ -0,0 +1,217 @@
+<template>
+    <div class="p-3">
+        <div class="bg-fff flex1 ov-hd d-flex flex-cln">
+            <div class="d-flex a-c pd-16 border-bottom">
+                <div class="f-s-20 c-333 f-w-7 mr-10">{{ form.id ? '编辑' : '新增' }}专家信息</div>
+                <el-button @click="router.go(-1)" type="primary" text>
+                    <el-icon>
+                        <Back />
+                    </el-icon>
+                    返回上一级
+                </el-button>
+            </div>
+            <div class="flex1 over-auto">
+                <el-form ref="formRef" label-width="auto" label-position="top" :model="form" :rules="rules">
+                    <div class="pd-16 border-bottom ov-hd">
+                        <el-row :gutter="20">
+                            <el-col :span="24">
+                                <el-form-item label="专家头像" prop="avatar">
+                                    <UploadAvatar v-model="form.avatar" shapeImg="square" shape="square"></UploadAvatar>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="专家姓名" prop="name">
+                                    <el-input v-model="form.name" placeholder="请输入专家姓名" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <!-- 性别 -->
+                            <el-col :span="6">
+                                <el-form-item label="性别" prop="sex">
+                                    <!-- <el-select v-model="form.sex" clearable placeholder="请选择性别">
+                                        <el-option v-for="item in sys_sex_type" :key="item.value" :label="item.label" :value="item.value" />
+                                    </el-select> -->
+                                    <!-- 单选框 -->
+                                    <el-radio-group v-model="form.sex">
+                                        <el-radio v-for="item in sys_sex_type" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
+                                    </el-radio-group>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="民族" prop="nation">
+                                    <el-input v-model="form.nation" placeholder="请输入民族" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="身份证号" prop="idCard">
+                                    <el-input v-model="form.idCard" placeholder="请输入身份证号" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="政治面貌" prop="politicalStatus">
+                                    <el-input v-model="form.politicalStatus" placeholder="请输入政治面貌" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="最高学历" prop="educationalBg">
+                                    <el-select v-model="form.educationalBg" clearable placeholder="请选择最高学历">
+                                        <el-option v-for="item in dm_educational_type" :key="item.value" :label="item.label" :value="item.value" />
+                                    </el-select>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="所学专业" prop="major">
+                                    <el-input v-model="form.major" placeholder="请输入所学专业" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="从事专业" prop="job">
+                                    <el-input v-model="form.job" placeholder="请输入从事专业" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="职称/职务" prop="post">
+                                    <el-input v-model="form.post" placeholder="请输入职称/职务" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="工作单位" prop="workUnit">
+                                    <el-input v-model="form.workUnit" placeholder="请输入工作单位" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="拟加入专家组" prop="personType">
+                                    <!-- <el-input v-model="form.personType" placeholder="请输入拟加入专家组" clearable /> -->
+                                    <el-select v-model="form.personType" clearable placeholder="请选择拟加入专家组" multiple>
+                                        <el-option v-for="item in dm_person_type" :key="item.value" :label="item.label" :value="item.value" />
+                                    </el-select>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="专业品种" prop="variety">
+                                    <el-input v-model="form.variety" placeholder="请输入专业品种" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="通信地址" prop="address">
+                                    <el-input v-model="form.address" placeholder="请输入通信地址" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="联系电话" prop="phone">
+                                    <el-input v-model="form.phone" placeholder="请输入联系电话" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="传真电话" prop="faxPhone">
+                                    <el-input v-model="form.faxPhone" placeholder="请输入传真电话" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="邮箱" prop="email">
+                                    <el-input v-model="form.email" placeholder="请输入邮箱" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="微信号" prop="wxNum">
+                                    <el-input v-model="form.wxNum" placeholder="请输入微信号" clearable />
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="岗位状态" prop="status">
+                                    <!-- <el-select v-model="form.status" clearable placeholder="请选择岗位状态">
+                                        <el-option label="在岗" value="1" />
+                                        <el-option label="离岗" value="0" />
+                                    </el-select> -->
+                                    <!-- // 单选框 -->
+                                    <el-radio-group v-model="form.status">
+                                        <el-radio v-for="item in dm_position_status" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
+                                    </el-radio-group>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="6">
+                                <el-form-item label="附件" prop="attachment">
+                                    <FileUpload v-model="form.attachment" format="array" :limit="10" :fileSize="100"></FileUpload>
+                                </el-form-item>
+                            </el-col>
+
+                            <el-col :span="24">
+                                <el-form-item label="简介" prop="brief">
+                                    <div class="flex1">
+                                        <Editor v-model="form.brief" placeholder="请输入内容"></Editor>
+                                    </div>
+                                </el-form-item>
+                            </el-col>
+                        </el-row>
+                    </div>
+                </el-form>
+            </div>
+            <div class="d-flex a-c j-c pd-16">
+                <el-button @click="router.go(-1)">取消</el-button>
+                <el-button @click="save" type="primary">提交</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup name="authority-input" lang="ts">
+import { ref, reactive, onMounted } from 'vue';
+import { debounce } from 'lodash';
+import { useRouter } from 'vue-router';
+import { expertPersonAdd, expertPersonUpdate, expertPersonDetail } from '@/api/authority';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_sex_type, dm_educational_type, dm_person_type, dm_position_status } = toRefs<any>(proxy?.useDict('sys_sex_type', 'dm_educational_type', 'dm_person_type', 'dm_position_status'));
+const router = useRouter();
+const route = useRoute();
+const form = ref<any>({
+    id: undefined,
+    avatar: '',
+    name: '',
+    sex: '0',
+    status: '1',
+    attachment: [],
+});
+const rules = reactive({
+    // 自动生成全部
+    avatar: [{ required: true, message: '请上传专家头像', trigger: 'blur' }],
+    name: [{ required: true, message: '请输入专家姓名', trigger: 'blur' }],
+    sex: [{ required: true, message: '请选择性别', trigger: 'change' }],
+    nation: [{ required: true, message: '请输入民族', trigger: 'blur' }],
+    idCard: [{ required: true, message: '请输入身份证号', trigger: 'blur' }],
+    politicalStatus: [{ required: true, message: '请输入政治面貌', trigger: 'blur' }],
+    educationalBg: [{ required: true, message: '请选择最高学历', trigger: 'change' }],
+    major: [{ required: true, message: '请输入所学专业', trigger: 'blur' }],
+    job: [{ required: true, message: '请输入从事专业', trigger: 'blur' }],
+    post: [{ required: true, message: '请输入职称/职务', trigger: 'blur' }],
+    workUnit: [{ required: true, message: '请输入工作单位', trigger: 'blur' }],
+    personType: [{ required: true, message: '请选择拟加入专家组', trigger: 'change' }],
+    variety: [{ required: true, message: '请输入专业品种', trigger: 'blur' }],
+    address: [{ required: true, message: '请输入通信地址', trigger: 'blur' }],
+    phone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }],
+    // faxPhone: [{ required: true, message: '请输入传真电话', trigger: 'blur' }],
+    // email: [{ required: true, message: '请输入邮箱', trigger: 'blur' }],
+    // wxNum: [{ required: true, message: '请输入微信号', trigger: 'blur' }],
+    status: [{ required: true, message: '请选择岗位状态', trigger: 'change' }],
+    brief: [{ required: true, message: '请输入简介', trigger: 'blur' }],
+
+});
+const formRef = ref();
+
+const save = debounce(async () => {
+    await formRef.value.validate();
+    const res = form.value.id ? await expertPersonUpdate(form.value) : await expertPersonAdd(form.value);
+    if (res && res.code === 200) {
+        router.go(-1);
+    }
+}, 500);
+// 获取专家详情
+const getExpertDetail = async () => {
+    if (route.query?.id) {
+        const res = await expertPersonDetail(route.query.id);
+        if (!res || res.code !== 200) return
+        form.value = res.data;
+    }
+};
+onMounted(() => {
+    getExpertDetail()
+});
+</script>

+ 177 - 0
src/views/authority/info/index.vue

@@ -0,0 +1,177 @@
+<template>
+    <div class="p-3">
+        <div class="bg-fff flex1 ov-hd d-flex flex-cln" v-show="showSearch">
+            <div class="pd-16 border-bottom">
+                <div class="f-s-20 c-333 f-w-7 mb-10">云药专家</div>
+                <div class="d-flex">
+                    <div class="flex1 ov-hd d-flex j-ed">
+                        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="auto">
+                            <el-form-item label="姓名" prop="name">
+                                <el-input v-model="queryParams.name" placeholder="搜姓名" clearable style="width: 180px" @keyup.enter="handleQuery" />
+                            </el-form-item>
+                            <el-form-item label="工作单位" prop="variety">
+                                <el-input v-model="queryParams.variety" placeholder="搜工作单位关键字" clearable style="width: 180px" @keyup.enter="handleQuery" />
+                            </el-form-item>
+                            <el-form-item label="拟加入专家组" prop="variety">
+                                <el-input v-model="queryParams.variety" placeholder="搜拟加入专家组" clearable style="width: 180px" @keyup.enter="handleQuery" />
+                            </el-form-item>
+                            <el-form-item label="专业品种" prop="variety">
+                                <el-input v-model="queryParams.variety" placeholder="搜专业品种" clearable style="width: 180px" @keyup.enter="handleQuery" />
+                            </el-form-item>
+                            <el-form-item label="前端显示状态" prop="putawayFlag">
+                                <el-select style="width: 140px" v-model="queryParams.putawayFlag" clearable placeholder="请选择状态">
+                                    <el-option label="显示" value="1" />
+                                    <el-option label="不显示" value="0" />
+                                </el-select>
+                            </el-form-item>
+                            <el-form-item>
+                                <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+                                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+                            </el-form-item>
+                        </el-form>
+                    </div>
+                </div>
+            </div>
+
+            <div class="d-flex flex1 ov-hd flex-cln pd-16">
+                <div class="d-flex j-sb mb-16">
+                    <div>
+                        <searchTabs v-model="queryParams.status" @change="handleQuery" :list="tabs" key-label="name" key-count="num" key-value="type"></searchTabs>
+                    </div>
+                    <el-button type="primary" @click="router.push({ path: 'authority-input' })">新增专家信息</el-button>
+                </div>
+                <div class="flex1 ov-hd">
+                    <vxe-table :loading="loading" border :data="dataList" min-height="0" max-height="100%">
+                        <vxe-column title="序号" align="center" type="seq" width="60" />
+                        <vxe-column title="姓名" align="center" field="name" :formatter="colNoData" />
+                        <vxe-column title="性别" width="70" align="center">
+                            <template #default="{ row }">
+                                {{ selectDictLabel(sys_sex_type, row?.sex) }}
+                            </template>
+                        </vxe-column>
+                        <vxe-column title="拟加入专家组">
+                            <template #default="{ row }">
+                                <DictTag :options="dm_person_type" :value="JSON.parse(row?.personType)"></DictTag>
+                            </template>
+                        </vxe-column>
+                        <vxe-column title="工作单位" field="workUnit" :formatter="colNoData" />
+                        <vxe-column title="职称/职务" field="post" :formatter="colNoData" />
+                        <vxe-column title="专业品种" field="variety" :formatter="colNoData" />
+                        <vxe-column title="岗位状态" width="90">
+                            <template #default="{ row }">
+                                <DictTag :options="dm_position_status" :value="row?.status"></DictTag>
+                            </template>
+                        </vxe-column>
+                        <vxe-column title="被抽取次数" width="100" field="chooseCount" :formatter="colNoData" />
+                        <vxe-column title="前端显示" width="90">
+                            <template #default="{ row }">
+                                <el-switch v-model="row.putawayFlag" active-value="1" inactive-value="0" @change="changeRowPutaway($event, row)"></el-switch>
+                            </template>
+                        </vxe-column>
+                        <vxe-column title="操作" width="250" align="center" fixed="right">
+                            <template #default="{ row }">
+                                <el-button type="primary" @click="editRow(row)" text>编辑</el-button>
+                                <el-button text type="danger" @click="deleteRow(row)">删除</el-button>
+                            </template>
+                        </vxe-column>
+                    </vxe-table>
+                </div>
+                <pagination :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup name="authority-info" lang="ts">
+import { colNoData } from '@/utils/noData';
+
+import { DateRange } from '@/views/models/index';
+import { searchTabs } from '@/views/models';
+import { expertList, expertPersonCount, expertPersonDelete, expertPersonOff, expertPersonOn } from '@/api/authority';
+const router = useRouter();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_sex_type, dm_educational_type, dm_person_type, dm_position_status } = toRefs<any>(proxy?.useDict('sys_sex_type', 'dm_educational_type', 'dm_person_type', 'dm_position_status'));
+const loading = ref(true);
+const showSearch = ref(true);
+const total = ref(0);
+const queryFormRef = ref<ElFormInstance>();
+const dataList = ref<any[]>([]);
+const initFormData = {};
+const data = reactive<any>({
+    form: { ...initFormData },
+    queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        status: ''
+    },
+    rules: {}
+});
+
+const { queryParams, form } = toRefs(data);
+/** 查询会员信息列表 */
+const getList = async () => {
+    loading.value = true;
+    const res = await expertList(queryParams.value);
+    dataList.value = res.rows;
+    total.value = res.total;
+    loading.value = false;
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+    queryParams.value.pageNum = 1;
+    getList();
+};
+// /** 切换前端显示状态 */
+const changeRowPutaway = async (value, row) => {
+    try {
+        if (+value) {
+            await expertPersonOn(row.id);
+            ElMessage.success('上架成功');
+        } else {
+            await expertPersonOff(row.id);
+            ElMessage.success('下架成功');
+        }
+        getList();
+    } catch (error) {
+        ElMessage.error('操作失败');
+    }
+};
+/** 重置按钮操作 */
+const resetQuery = () => {
+    queryFormRef.value?.resetFields();
+    handleQuery();
+};
+
+/** 编辑新闻 */
+const editRow = (row) => {
+    router.push({ path: 'authority-input', query: { id: row.id } });
+};
+
+/** 删除新闻 */
+const deleteRow = async (row) => {
+    ElMessageBox.confirm(`确认要删除专家为"${row.name}"的数据吗?`, '删除提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning'
+    }).then(async () => {
+        const res = await expertPersonDelete([row.id]);
+        if (res) {
+            ElMessage.success('删除成功');
+            getList();
+        }
+    });
+};
+// 获取专家统计数量
+const tabs = ref([]);
+const getExpertPersonCount = async () => {
+    const res = await expertPersonCount();
+    if (res?.code === 200) {
+        tabs.value = res.data
+    }
+};
+onMounted(() => {
+    getExpertPersonCount();
+    getList();
+});
+</script>