yanghao 1 miesiąc temu
rodzic
commit
ec36fe38f8

+ 1 - 1
.env.local

@@ -4,7 +4,7 @@ NODE_ENV=development
 VITE_DEV=true
 
 # 请求路径
-VITE_BASE_URL='http://172.21.10.129:38080'
+VITE_BASE_URL='http://192.168.188.86:38080'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server

+ 24 - 0
src/api/expert/index.ts

@@ -19,3 +19,27 @@ export const updateExpert = (data: any) => {
 export const getExpertDetail = (id: number) => {
   return request.get({ url: `/rd/expert-pool/get?id=${id}` })
 }
+
+/**
+ * 团队管理
+ */
+
+export const getTeamList = (params) => {
+  return request.get({ url: '/rd/team/page', params })
+}
+
+export const createTeam = (data: any) => {
+  return request.post({ url: '/rd/team/create', data })
+}
+
+export const updateTeam = (data: any) => {
+  return request.put({ url: '/rd/team/update', data })
+}
+
+export const deleteTeam = (id: number) => {
+  return request.delete({ url: `/rd/team/delete?id=${id}` })
+}
+
+export const getTeamDetail = (id: number) => {
+  return request.get({ url: `/rd/team/get?id=${id}` })
+}

+ 5 - 16
src/views/expert/list/index.vue

@@ -253,7 +253,7 @@
                   v-for="dict in getStrDictOptions(DICT_TYPE.RD_EXPERT_RANK)"
                   :key="dict.value"
                   :label="dict.label"
-                  :value="dict.value"
+                  :value="Number(dict.value)"
                 />
               </el-select>
             </el-form-item>
@@ -297,7 +297,7 @@
                   v-for="dict in getStrDictOptions(DICT_TYPE.RD_EXPERT_TECHNICAL)"
                   :key="dict.value"
                   :label="dict.label"
-                  :value="dict.value"
+                  :value="Number(dict.value)"
                 />
               </el-select>
             </el-form-item>
@@ -317,19 +317,14 @@
                   v-for="dict in getStrDictOptions(DICT_TYPE.RD_EXPERT_SOFTWARE)"
                   :key="dict.value"
                   :label="dict.label"
-                  :value="dict.value"
+                  :value="Number(dict.value)"
                 />
               </el-select>
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="所属团队" prop="teamIds">
-              <el-select
-                v-model="formData.teamIds"
-                placeholder="请选择所属团队"
-                clearable
-                multiple
-              >
+              <el-select v-model="formData.teamIds" placeholder="请选择所属团队" clearable multiple>
                 <el-option
                   v-for="team in formTeamOptions"
                   :key="team.value"
@@ -697,13 +692,6 @@ const wordCloudWords = [
   { text: '研究生', size: 16, rotate: -5, color: '#7f85b5', left: '79%', top: '28%' }
 ]
 
-const teamOptions = [
-  { label: '全部', value: '' },
-  { label: '第一梯队', value: '第一梯队' },
-  { label: '第二梯队', value: '第二梯队' },
-  { label: '第三梯队', value: '第三梯队' }
-]
-
 const formTeamOptions = [
   { label: '技术评审组', value: 1 },
   { label: '经营评审组', value: 2 },
@@ -791,6 +779,7 @@ const submitForm = async () => {
 
   submitLoading.value = true
   try {
+    formData.value.avatar = formData.value.avatar.path
     await createExpert({ ...formData.value })
     ElMessage.success('新增成功')
     closeDialog()

+ 330 - 2
src/views/expert/team/index.vue

@@ -1,5 +1,333 @@
 <template>
-  <div class="list"> team </div>
+  <ContentWrap>
+    <el-form
+      ref="queryFormRef"
+      :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
+      label-width="96px"
+    >
+      <el-form-item label="团队所属公司" prop="deptId">
+        <el-tree-select
+          v-model="queryParams.deptId"
+          :data="deptList"
+          :props="defaultProps"
+          check-strictly
+          clearable
+          filterable
+          node-key="id"
+          placeholder="请选择团队所属公司"
+          style="width: 240px"
+        />
+      </el-form-item>
+      <el-form-item label="团队名称" prop="teamName">
+        <el-input
+          v-model="queryParams.teamName"
+          clearable
+          placeholder="请输入团队名称"
+          class="!w-240px"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px" />
+          重置
+        </el-button>
+        <el-button type="primary" plain @click="openForm('create')">
+          <Icon icon="ep:plus" class="mr-5px" />
+          新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="团队所属公司" align="center" min-width="180">
+        <template #default="{ row }">
+          {{ getDeptName(row.deptId) || '-' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="团队名称" align="center" prop="teamName" min-width="180" />
+      <el-table-column label="团队 Logo" align="center" width="120">
+        <template #default="{ row }">
+          <el-image
+            v-if="row.avatar"
+            :src="row.avatar"
+            fit="cover"
+            class="team-logo"
+            :preview-src-list="[row.avatar]"
+            preview-teleported
+          />
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="团队概述" align="center" prop="summary" min-width="260" show-overflow-tooltip />
+      <el-table-column label="创建时间" align="center" prop="createTime" min-width="180">
+        <template #default="{ row }">
+          {{ formatDate(row.createTime) }}
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="160" fixed="right">
+        <template #default="{ row }">
+          <el-button link type="primary" @click="openForm('update', row.id)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(row.id)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <Dialog
+    v-model="dialogVisible"
+    :title="dialogTitle"
+    width="640px"
+    destroy-on-close
+    @close="closeDialog"
+  >
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="110px"
+    >
+      <el-form-item label="团队所属公司" prop="deptId">
+        <el-tree-select
+          v-model="formData.deptId"
+          :data="deptList"
+          :props="defaultProps"
+          check-strictly
+          clearable
+          filterable
+          node-key="id"
+          placeholder="请选择团队所属公司"
+        />
+      </el-form-item>
+      <el-form-item label="团队名称" prop="teamName">
+        <el-input v-model="formData.teamName" placeholder="请输入团队名称" />
+      </el-form-item>
+      <el-form-item label="团队 Logo" prop="avatar">
+        <UploadImg v-model="formData.avatar" height="80px" width="80px" />
+      </el-form-item>
+      <el-form-item label="团队概述" prop="summary">
+        <el-input
+          v-model="formData.summary"
+          type="textarea"
+          :rows="4"
+          maxlength="500"
+          show-word-limit
+          placeholder="请输入团队概述"
+        />
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="closeDialog">取 消</el-button>
+      <el-button type="primary" :loading="submitLoading" @click="submitForm">确 定</el-button>
+    </template>
+  </Dialog>
 </template>
 
-<script setup></script>
+<script setup lang="ts">
+import { computed, onMounted, reactive, ref } from 'vue'
+import { ElMessage, ElMessageBox, FormRules } from 'element-plus'
+import { getTeamList, createTeam, updateTeam, deleteTeam, getTeamDetail } from '@/api/expert'
+import * as DeptApi from '@/api/system/dept'
+import { defaultProps } from '@/utils/tree'
+import { formatDate } from '@/utils/formatTime'
+
+type TeamFormData = {
+  id?: number
+  deptId?: number
+  teamName: string
+  avatar: string
+  summary: string
+}
+
+const defaultFormData = (): TeamFormData => ({
+  id: undefined,
+  deptId: undefined,
+  teamName: '',
+  avatar: '',
+  summary: ''
+})
+
+const loading = ref(false)
+const total = ref(0)
+const list = ref<any[]>([])
+const deptList = ref<Tree[]>([])
+const dialogVisible = ref(false)
+const dialogTitle = ref('')
+const formLoading = ref(false)
+const submitLoading = ref(false)
+const queryFormRef = ref()
+const formRef = ref()
+const formType = ref<'create' | 'update'>('create')
+const formData = ref<TeamFormData>(defaultFormData())
+
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  deptId: undefined as number | undefined,
+  teamName: undefined as string | undefined
+})
+
+const formRules = reactive<FormRules>({
+  deptId: [{ required: true, message: '请选择团队所属公司', trigger: 'change' }],
+  teamName: [{ required: true, message: '请输入团队名称', trigger: 'blur' }],
+  avatar: [{ required: true, message: '请上传团队 Logo', trigger: 'change' }],
+  summary: [{ required: true, message: '请输入团队概述', trigger: 'blur' }]
+})
+
+const deptMap = computed(() => {
+  const map = new Map<number, string>()
+  const walk = (nodes: any[]) => {
+    nodes.forEach((node) => {
+      map.set(node.id, node.name)
+      if (node.children?.length) {
+        walk(node.children)
+      }
+    })
+  }
+  walk(deptList.value || [])
+  return map
+})
+
+const getDeptName = (deptId?: number) => {
+  if (deptId === undefined || deptId === null) return ''
+  return deptMap.value.get(deptId) || ''
+}
+
+const getDeptOptions = async () => {
+  deptList.value = await DeptApi.getSimpleDeptList()
+}
+
+const getList = async () => {
+  loading.value = true
+  try {
+    const res = await getTeamList(queryParams)
+    list.value = res.list || []
+    total.value = res.total || 0
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleQuery = async () => {
+  queryParams.pageNo = 1
+  await getList()
+}
+
+const resetQuery = async () => {
+  queryFormRef.value?.resetFields()
+  queryParams.pageNo = 1
+  await getList()
+}
+
+const resetForm = () => {
+  formData.value = defaultFormData()
+  formRef.value?.clearValidate()
+}
+
+const closeDialog = () => {
+  dialogVisible.value = false
+  resetForm()
+}
+
+const normalizeDetail = (data: any): TeamFormData => ({
+  id: data.id,
+  deptId: data.deptId,
+  teamName: data.teamName || '',
+  avatar: typeof data.avatar === 'string' ? data.avatar : data.avatar?.path || '',
+  summary: data.summary || ''
+})
+
+const openForm = async (type: 'create' | 'update', id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = type === 'create' ? '新增团队' : '编辑团队'
+  formType.value = type
+  resetForm()
+
+  if (type === 'update' && id) {
+    formLoading.value = true
+    try {
+      const detail = await getTeamDetail(id)
+      formData.value = normalizeDetail(detail)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+
+const submitForm = async () => {
+  if (!formRef.value) return
+
+  const valid = await formRef.value.validate().catch(() => false)
+  if (!valid) return
+
+  submitLoading.value = true
+  try {
+    const payload = {
+      ...formData.value,
+      avatar:
+        typeof formData.value.avatar === 'string'
+          ? formData.value.avatar
+          : (formData.value.avatar as any)?.path || ''
+    }
+
+    if (formType.value === 'create') {
+      await createTeam(payload)
+      ElMessage.success('新增成功')
+    } else {
+      await updateTeam(payload)
+      ElMessage.success('修改成功')
+    }
+
+    closeDialog()
+    await getList()
+  } finally {
+    submitLoading.value = false
+  }
+}
+
+const handleDelete = async (id: number) => {
+  try {
+    await ElMessageBox.confirm('确定要删除该团队吗?', '提示', {
+      type: 'warning',
+      confirmButtonText: '确定',
+      cancelButtonText: '取消'
+    })
+    await deleteTeam(id)
+    ElMessage.success('删除成功')
+    await getList()
+  } catch (error) {
+    // ignore
+  }
+}
+
+onMounted(async () => {
+  await getDeptOptions()
+  await getList()
+})
+</script>
+
+<style scoped lang="scss">
+.team-logo {
+  width: 52px;
+  height: 52px;
+  border-radius: 8px;
+  border: 1px solid #e5e7eb;
+}
+</style>