瀏覽代碼

Merge branch 'dev' of https://git.citupro.com/zhengnaiwen_citu/menduner-admin into dev

lifanagju_citu 3 月之前
父節點
當前提交
d7fe45c2b8
共有 26 個文件被更改,包括 1381 次插入85 次删除
  1. 5 0
      src/api/menduner/system/analysis/statisticAnalysis.ts
  2. 2 2
      src/api/menduner/system/jobFair/white/index.ts
  3. 49 0
      src/api/menduner/system/menu/index.ts
  4. 58 0
      src/api/menduner/system/vote/index.ts
  5. 17 0
      src/router/modules/remaining.ts
  6. 6 6
      src/views/mall/promotion/lottery/config/LuckLotteryForm.vue
  7. 3 3
      src/views/mall/promotion/lottery/config/index.vue
  8. 0 6
      src/views/menduner/system/analysis/statisticAnalysis/components/AgeDistribution.vue
  9. 1 3
      src/views/menduner/system/analysis/statisticAnalysis/components/Education.vue
  10. 4 6
      src/views/menduner/system/analysis/statisticAnalysis/components/SexDistribution.vue
  11. 0 2
      src/views/menduner/system/analysis/statisticAnalysis/components/WorkExperience.vue
  12. 21 0
      src/views/menduner/system/analysis/statisticAnalysis/index.vue
  13. 10 11
      src/views/menduner/system/enterprise/message/SetVip.vue
  14. 11 4
      src/views/menduner/system/enterprise/message/details/components/info.vue
  15. 44 6
      src/views/menduner/system/enterprise/message/index.vue
  16. 257 0
      src/views/menduner/system/enterpriseMenu/MenuForm.vue
  17. 214 0
      src/views/menduner/system/enterpriseMenu/index.vue
  18. 9 10
      src/views/menduner/system/jobFair/manage/details/index.vue
  19. 8 18
      src/views/menduner/system/jobFair/manage/details/jobFairForm.vue
  20. 178 0
      src/views/menduner/system/vote/VotingActivityJoinItem.vue
  21. 140 0
      src/views/menduner/system/vote/VotingActivityJoinItemForm.vue
  22. 152 0
      src/views/menduner/system/vote/index.vue
  23. 180 0
      src/views/menduner/system/vote/votingActivityForm.vue
  24. 2 2
      src/views/menduner/system/web/PreferredGroup.vue
  25. 8 5
      src/views/menduner/system/web/WebContentForm.vue
  26. 2 1
      src/views/menduner/system/web/index.vue

+ 5 - 0
src/api/menduner/system/analysis/statisticAnalysis.ts

@@ -12,6 +12,11 @@ export const statisticAnalysisApi = {
     return await request.get({ url: `/menduner/system/analysis/get/job/browse/num/page`, params })
   },
 
+  // 职位刷新量统计分析
+  getAnalysisJobRefreshPage: async (params: any) => {
+    return await request.get({ url: `/menduner/system/analysis/get/job/refresh/page`, params })
+  },
+
   // 获取发布职位统计分析明细
   getAnalysisJobNumPage: async (params: any) => {
     return await request.get({ url: `/menduner/system/analysis/get/job/num/page`, params })

+ 2 - 2
src/api/menduner/system/jobFair/white/index.ts

@@ -9,12 +9,12 @@ export const JobFairWhiteApi = {
 
   // 添加企业白名单
   addJobFairWhiteList: async (data: any) => {
-    return await request.put({ url: `/menduner/system/job-fair/add-white-list2`, data })
+    return await request.put({ url: `/menduner/system/job-fair/add-white-list`, data })
   },
 
   // 移除企业白名单
   removeJobFairWhiteList: async (data: any) => {
-    return await request.put({ url: `/menduner/system/job-fair/remove-white-list2`, data })
+    return await request.put({ url: `/menduner/system/job-fair/remove-white-list`, data })
   },
 
   // 导出参加双选会职位 Excel

+ 49 - 0
src/api/menduner/system/menu/index.ts

@@ -0,0 +1,49 @@
+import request from '@/config/axios'
+
+export interface MenuVO {
+  id: number
+  name: string
+  permission: string
+  type: number
+  sort: number
+  parentId: number
+  path: string
+  icon: string
+  component: string
+  componentName?: string
+  status: number
+  visible: boolean
+  keepAlive: boolean
+  alwaysShow?: boolean
+  createTime: Date
+}
+
+// 查询菜单(精简)列表
+export const getSimpleMenusList = () => {
+  return request.get({ url: '/menduner/system/menu/simple-list' })
+}
+
+// 查询菜单列表
+export const getMenuList = (params) => {
+  return request.get({ url: '/menduner/system/menu/list', params })
+}
+
+// 获取菜单详情
+export const getMenu = (id: number) => {
+  return request.get({ url: '/menduner/system/menu/get?id=' + id })
+}
+
+// 新增菜单
+export const createMenu = (data: MenuVO) => {
+  return request.post({ url: '/menduner/system/menu/create', data })
+}
+
+// 修改菜单
+export const updateMenu = (data: MenuVO) => {
+  return request.put({ url: '/menduner/system/menu/update', data })
+}
+
+// 删除菜单
+export const deleteMenu = (id: number) => {
+  return request.delete({ url: '/menduner/system/menu/delete?id=' + id })
+}

+ 58 - 0
src/api/menduner/system/vote/index.ts

@@ -0,0 +1,58 @@
+import request from '@/config/axios'
+
+export const VotingActivityApi = {
+	// 查询投票活动分页
+	getVotingActivityPage: async (params: any) => {
+		return await request.get({ url: `/menduner/system/mde/voting-activity/page`, params })
+	},
+
+	// 查询投票活动
+	getVotingActivity: async (id: number) => {
+		return await request.get({ url: `/menduner/system/mde/voting-activity/get?id=` + id })
+	},
+
+	// 获取候选人类型
+	getVotingActivityCategories: async (id: number) => {
+		return await request.get({ url: `/menduner/system/mde/voting-activity-join-item/categorys?votingActivityId=` + id })
+	},
+
+	// 创建投票活动
+	createVotingActivity: async (data: any) => {
+		return await request.post({ url: `/menduner/system/mde/voting-activity/create`, data })
+	},
+
+	// 更新投票活动
+	updateVotingActivity: async (data: any) => {
+		return await request.put({ url: `/menduner/system/mde/voting-activity/update`, data })
+	},
+
+	// 删除投票活动
+	deleteVotingActivity: async (id: number) => {
+		return await request.delete({ url: `/menduner/system/mde/voting-activity/delete?id=` + id })
+	},
+
+	// 查询投票活动候选人分页
+	getVotingActivityJoinItemPage: async (params: any) => {
+		return await request.get({ url: `/menduner/system/mde/voting-activity-join-item/page`, params })
+	},
+
+	// 查询投票活动候选人
+	getVotingActivityJoinItem: async (id: number) => {
+		return await request.get({ url: `/menduner/system/mde/voting-activity-join-item/get?id=` + id })
+	},
+	
+	// 创建投票活动候选人
+	createVotingActivityJoinItem: async (data: any) => {
+		return await request.post({ url: `/menduner/system/mde/voting-activity-join-item/create`, data })
+	},
+
+	// 更新投票活动候选人
+	updateVotingActivityJoinItem: async (data: any) => {
+		return await request.put({ url: `/menduner/system/mde/voting-activity-join-item/update`, data })
+	},
+
+	// 删除投票活动候选人
+	deleteVotingActivityJoinItem: async (id: number) => {
+		return await request.delete({ url: `/menduner/system/mde/voting-activity-join-item/delete?id=` + id })
+	},
+}

+ 17 - 0
src/router/modules/remaining.ts

@@ -548,6 +548,23 @@ const remainingRouter: AppRouteRecordRaw[] = [
         component: () => import('@/views/menduner/system/jobFair/manage/statistics/index.vue')
       }
     ]
+  },{
+    path: '/voting-management',
+    component: Layout,
+    name: 'VotingActivityManage',
+    meta: { hidden: true },
+    children: [
+      {
+        path: ':id',
+        name: 'VotingActivityJoinItem',
+        meta: {
+          title: '投票活动详情',
+          noCache: true,
+          hidden: true
+        },
+        component: () => import('@/views/menduner/system/vote/VotingActivityJoinItem.vue')
+      }
+    ]
   },
   {
     path: '/order',

+ 6 - 6
src/views/mall/promotion/lottery/config/LuckLotteryForm.vue

@@ -35,10 +35,10 @@
       <el-form-item label="结束时间" prop="endTime">
         <el-date-picker v-model="formData.endTime" type="date"  value-format="x" placeholder="选择结束时间" />
       </el-form-item>
-      <el-form-item label="活动文案" prop="content">
+      <!-- <el-form-item label="活动文案" prop="content">
         <Editor v-model="formData.content" height="150px" />
-      </el-form-item>
-      <el-form-item label="中奖记录展示" prop="isAllRecord">
+      </el-form-item> -->
+      <!-- <el-form-item label="中奖记录展示" prop="isAllRecord">
         <el-switch v-model="formData.isAllRecord" />
       </el-form-item>
       <el-form-item label="个人中奖纪录展示" prop="isPersonalRecord">
@@ -46,10 +46,10 @@
       </el-form-item>
       <el-form-item label="活动规格是否展示" prop="isContent">
         <el-switch v-model="formData.isContent" />
-      </el-form-item>
-      <el-form-item label="排序" prop="sort">
+      </el-form-item> -->
+      <!-- <el-form-item label="排序" prop="sort">
         <el-input v-model="formData.sort" placeholder="请输入排序" />
-      </el-form-item>
+      </el-form-item> -->
       <el-form-item label="状态" prop="status">
         <el-select v-model="formData.status" placeholder="请选择状态" clearable>
           <el-option v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" :key="dict.value" :label="dict.label" :value="dict.value.toString()" />

+ 3 - 3
src/views/mall/promotion/lottery/config/index.vue

@@ -104,7 +104,7 @@
       <el-table-column label="开始时间" align="center" prop="startTime" :formatter="dateFormatter2" width="110px" />
       <el-table-column label="结束时间" align="center" prop="endTime" :formatter="dateFormatter2" width="110px" />
       <el-table-column label="获取一次抽奖的条件数量" align="center" prop="factorNum" />
-      <el-table-column label="中奖记录展示" align="center" prop="isAllRecord">
+      <!-- <el-table-column label="中奖记录展示" align="center" prop="isAllRecord">
         <template #default="scope">{{ scope.row.isAllRecord ? '是' : '否'}}</template>
       </el-table-column>
       <el-table-column label="个人中奖记录展示" align="center" prop="isPersonalRecord">
@@ -112,8 +112,8 @@
       </el-table-column>
       <el-table-column label="活动规格是否展示" align="center" prop="isContent">
         <template #default="scope">{{ scope.row.isContent ? '是' : '否'}}</template>
-      </el-table-column>
-      <el-table-column label="排序" align="center" prop="sort" />
+      </el-table-column> -->
+      <!-- <el-table-column label="排序" align="center" prop="sort" /> -->
       <el-table-column label="状态" align="center" prop="status">
         <template #default="scope">
           <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />

+ 0 - 6
src/views/menduner/system/analysis/statisticAnalysis/components/AgeDistribution.vue

@@ -62,10 +62,4 @@ watch(() => props.data, (newVal: any) => {
   chartOptions.xAxis.data = newVal.x || []
   chartOptions.series![0].data = newVal.y || []
 })
-const EChartClick = (obj) => {
-  console.log('1', obj)
-  debugger
-}
 </script>
-<style lang="scss" scoped>
-</style>

+ 1 - 3
src/views/menduner/system/analysis/statisticAnalysis/components/Education.vue

@@ -61,6 +61,4 @@ watch(() => props.data, (newVal: any) => {
   chartOptions.xAxis.data = newVal.x || []
   chartOptions.series![0].data = newVal.y || []
 })
-</script>
-<style lang="scss" scoped>
-</style>
+</script>

+ 4 - 6
src/views/menduner/system/analysis/statisticAnalysis/components/SexDistribution.vue

@@ -36,16 +36,16 @@ const chartOptions = reactive<EChartsOption>({
       name: props.title,
       type: 'pie',
       radius: '50%',
-      data: [
-        // { value: 400, name: '男' },
-        // { value: 100, name: '女' },
-      ],
+      data: [],
       emphasis: {
         itemStyle: {
           shadowBlur: 10,
           shadowOffsetX: 0,
           shadowColor: 'rgba(0, 0, 0, 0.5)'
         }
+      },
+      label: {
+        formatter: '{b} : {c}人 (占比:{d}%)'
       }
     }
   ]
@@ -59,5 +59,3 @@ watch(() => props.data, (newVal) => {
     }
 })
 </script>
-<style lang="scss" scoped>
-</style>

+ 0 - 2
src/views/menduner/system/analysis/statisticAnalysis/components/WorkExperience.vue

@@ -62,5 +62,3 @@ watch(() => props.data, (newVal: any) => {
   chartOptions.series![0].data = newVal.y || []
 })
 </script>
-<style lang="scss" scoped>
-</style>

+ 21 - 0
src/views/menduner/system/analysis/statisticAnalysis/index.vue

@@ -253,6 +253,7 @@ const apiArr = reactive({
   invitedCompleted: statisticAnalysisApi.getAnalysisInterviewCompletePage, // 钻取
   userLoginNum: statisticAnalysisApi.getLoginUserCount, // 钻取
   enterpriseUserLoginNum: statisticAnalysisApi.getLoginEnterpriseUserCount, // 钻取
+  refreshJobNum: statisticAnalysisApi.getAnalysisJobRefreshPage, // 钻取
 
   // 分布
   sexDistributionData: statisticAnalysisApi.getAnalysisJobCvSexCount,
@@ -268,6 +269,7 @@ const statisticList = [
   { title: '所有职位数量', name: 'pushTotalNum' },
   { title: '发布中职位数量', name: 'pushNum' },
   { title: '职位浏览量', name: 'pageViews' },
+  { title: '刷新职位', name: 'refreshJobNum' },
   { title: '收到的简历', name: 'resumeReceived' },
   { title: '已查看简历', name: 'resumeViewed' },
   { title: '已邀面试', name: 'invitedInterviews' },
@@ -280,6 +282,7 @@ const statistic = reactive({
   pushNum: 0,
   resumeReceived: 0,
   resumeViewed: 0,
+  refreshJobNum: 0,
   invitedInterviews: 0,
   invitedCompleted: 0,
   userLoginNum: 0,
@@ -412,6 +415,13 @@ const tableHeaders = {
     { name: '学历要求', prop: 'eduName' },
     { name: '众聘', prop: 'hire' },
   ],
+  // 刷新职位
+  refreshJobNum: [
+    { name: '职位名称', prop: 'name' },
+    { name: '发布企业', prop: 'enterpriseName' },
+    { name: '刷新时间', prop: 'refreshTime' },
+    { name: '发布时间', prop: 'createTime' },
+  ],
   // 收到的简历
   resumeReceived: [
     { name: '投递人', prop: 'personName' },
@@ -462,6 +472,7 @@ const tableHeaders = {
   enterpriseUserLoginNum: [
     { name: '企业名称', prop: 'enterpriseName' },
     { name: '企业用户', prop: 'username' },
+    { name: '登录邮箱', prop: 'userBind.email' },
     { name: '企业用户联系电话', prop: 'userBind.phone' },
     { name: '登录IP', prop: 'loginLog.userIp' },
     { name: '登录时间', prop: 'loginTime' }
@@ -516,6 +527,16 @@ const dealTableData = async () => {
       return item
     })
   }
+  // 职位刷新
+  if (currentItem.value.name === 'refreshJobNum') {
+    tableData.value = tableData.value.map(item => {
+      item.name = formatName(item.name)
+      item.enterpriseName = formatName(item.enterpriseAnotherName || item.enterpriseName)
+      item.refreshTime = timesTampChange(item.refreshTime)
+      item.createTime = timesTampChange(item.createTime)
+      return item
+    })
+  }
   // 收到的简历
   if (currentItem.value.name === 'resumeReceived') {
     const areaList = await getDictOptions('areaList')

+ 10 - 11
src/views/menduner/system/enterprise/message/SetVip.vue

@@ -3,6 +3,7 @@
 		<el-descriptions class="mb-50px" title="企业当前权益" :column="2" border>
 			<el-descriptions-item label="是否开启人才地图">{{ info.personMap ? '是' : '否' }}</el-descriptions-item>
 			<el-descriptions-item label="是否允许发布众聘职位">{{ info.hireJob ? '是' : '否' }}</el-descriptions-item>
+			<el-descriptions-item label="是否允许参加招聘会">{{ info.jobFair ? '是' : '否' }}</el-descriptions-item>
 			<!-- <el-descriptions-item label="是否开启新任命">{{ info.newAppointment ? '是' : '否' }}</el-descriptions-item> -->
 			<el-descriptions-item label="剩余发布职位数量">{{ info.publishJobCount || 0 }}个</el-descriptions-item>
 			<el-descriptions-item label="剩余搜索人才数量">{{ info.searchCount || 0 }}次</el-descriptions-item>
@@ -18,12 +19,12 @@
     >
       <el-form-item label="会员套餐" prop="packageId">
         <el-select v-model="formData.packageId" placeholder="请选择会员套餐" @change="handleChange">
-					<el-option v-for="val in packList"	:key="val.id" :label="val.name + ': ' + val.text + `(会员时效增加:${val.day}天)`" :value="val.id" />
+					<el-option v-for="val in packageList"	:key="val.id" :label="val.text + `(会员时效增加:${val.day}天)`" :value="val.id" />
 				</el-select>
       </el-form-item>
       <el-form-item label="是否激活VIP" prop="entitlement.activateVip" :rules="[{ required: true, message: '请设置是否激活VIP', trigger: 'change' }]">
         <el-switch v-model="formData.entitlement.activateVip" />
-        vip激活后到期,权益将无法使用;vip未激活,权益可以持续一直使用
+        vip激活后到期,权益将无法使用;vip未激活,权益可以持续至额度归零
       </el-form-item>
       <el-form-item v-if="formData.entitlement.activateVip" label="VIP过期时间" prop="entitlement.vipExpireDate" :rules="[{ required: true, message: '请选择VIP过期时间', trigger: 'change' }]">
         <el-date-picker v-model="formData.entitlement.vipExpireDate" :disabledDate="disabledDates" value-format="x" type="date" placeholder="请选择VIP过期时间" />
@@ -43,6 +44,9 @@
       <el-form-item label="允许发布众聘职位" prop="entitlement.hireJob" :rules="[{ required: true, message: '请设置是否允许发布众聘职位', trigger: 'change' }]">
         <el-switch v-model="formData.entitlement.hireJob" />
       </el-form-item>
+      <el-form-item label="允许参加招聘会" prop="entitlement.jobFair" :rules="[{ required: true, message: '请设置是否允许参加招聘会', trigger: 'change' }]">
+        <el-switch v-model="formData.entitlement.jobFair" />
+      </el-form-item>
       <!-- <el-form-item label="开启新任命" prop="entitlement.newAppointment" :rules="[{ required: true, message: '请设置是否开启新任命', trigger: 'change' }]">
         <el-switch v-model="formData.entitlement.newAppointment" />
       </el-form-item> -->
@@ -58,6 +62,7 @@
 defineOptions({ name: 'EnterpriseMessageSetVip' })
 import { EnterpriseApi } from '@/api/menduner/system/enterprise/message'
 
+const props = defineProps({ packageList: Array })
 const message = useMessage() // 消息弹窗
 const dialogVisible = ref(false) // 弹窗的是否展示
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
@@ -72,23 +77,17 @@ const formData = ref({
     lookCvCount: 0,
     personMap: false,
     hireJob: false,
+    jobFair: false,
     // newAppointment: false
   }
 })
 const formRef = ref()
 
-// 套餐列表
-const packList = ref([])
-const getPackList = async () => {
-	const data = await EnterpriseApi.getEnterprisePackageList()
-	packList.value = data
-}
-
 // 套餐选择
 const item = ref({})
 const handleChange = (val) => {
   if (!val) return
-  const obj = packList.value.find(e => e.id === val)
+  const obj = props.packageList.value.find(e => e.id === val)
   for (let key in formData.value.entitlement) {
     // 会员标识激活时才设置vip过期时间
     if (key === 'vipExpireDate' && obj.activateVip) {
@@ -112,7 +111,6 @@ const disabledDates = (date) => {
 /** 打开弹窗 */
 const info = ref({})
 const open = async (id: number, vipExpireDate) => {
-	await getPackList()
   dialogVisible.value = true
   resetForm()
 	formData.value.enterpriseId = id
@@ -172,6 +170,7 @@ const resetForm = () => {
       lookCvCount: 0,
       personMap: false,
       hireJob: false,
+      jobFair: false,
       // newAppointment: false
     }
   }

+ 11 - 4
src/views/menduner/system/enterprise/message/details/components/info.vue

@@ -1,9 +1,6 @@
 <template>
   <div>
     <el-descriptions class="margin-top" :column="2" border>
-      <!-- <template #extra>
-        <el-button type="primary">Operation</el-button>
-      </template> -->
     <el-descriptions-item label="企业LOGO">
       <el-image v-if="info.logoUrl" style="width: 100px; height: 100px" :src="info.logoUrl" fit="contain" hide-on-click-modal :preview-src-list="[info.logoUrl]"/>
     </el-descriptions-item>
@@ -26,7 +23,9 @@
     <el-descriptions-item label="企业标签">
       <el-tag type="primary" v-for="k in info.tagList" :key="k" class="m-r-5px">{{ k }}</el-tag>
     </el-descriptions-item>
-    <el-descriptions-item label="会员">{{ info.vipFlag && Number(info.vipFlag) > 0 ? '会员' : '非会员' }}</el-descriptions-item>
+    <el-descriptions-item label="会员">
+      {{ info.vipExpireDate ? info.vipExpireDate > Date.now() ? packageList.find(e => e.id === Number(info.vipFlag))?.text : '会员已过期' : '非会员' }}
+    </el-descriptions-item>
     <el-descriptions-item label="会员到期时间">{{ formatDate(info.vipExpireDate, 'YYYY-MM-DD') }}</el-descriptions-item>
   </el-descriptions>
   </div>
@@ -49,4 +48,12 @@ const getInfo = async () => {
   info.value = data
 }
 getInfo()
+
+// 套餐列表
+const packageList = ref([])
+const getPackList = async () => {
+	const data = await EnterpriseApi.getEnterprisePackageList()
+	packageList.value = data
+}
+getPackList()
 </script>

+ 44 - 6
src/views/menduner/system/enterprise/message/index.vue

@@ -6,7 +6,7 @@
       :model="queryParams"
       ref="queryFormRef"
       :inline="true"
-      label-width="68px"
+      label-width="100px"
     >
       <el-form-item label="企业全称" prop="name" v-hasPermi="['menduner:system:enterprise:query']">
         <el-input
@@ -55,6 +55,32 @@
           class="!w-240px"
         />
       </el-form-item>
+      <el-form-item label="会员套餐" prop="vipFlag">
+        <el-select
+          v-model="queryParams.vipFlag"
+          placeholder="请选择会员套餐"
+          clearable
+          class="!w-240px"
+        >
+          <el-option
+            v-for="dict in packageList"
+            :key="dict.id"
+            :label="dict.text"
+            :value="dict.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="会员过期时间" prop="vipExpireDate">
+        <el-date-picker
+          v-model="queryParams.vipExpireDate"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          type="daterange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-240px"
+        />
+      </el-form-item>
       <el-form-item>
         <el-button v-hasPermi="['menduner:system:enterprise:query']" @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
         <el-button v-hasPermi="['menduner:system:enterprise:query']" @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
@@ -93,9 +119,11 @@
       <el-table-column label="地址" align="center" prop="address" />
       <el-table-column label="会员" align="center" prop="vipFlag">
         <template #default="{ row }">
-          <span :style="{'color': row.vipExpireDate ? row.vipExpireDate > Date.now() ? '#67C23A' : '#E6A23C' : ''}">
-            {{ row.vipExpireDate ? row.vipExpireDate > Date.now() ? '会员' : '过期' : '非会员' }}
-          </span>
+          <span v-if="row.vipExpireDate && row.vipExpireDate > Date.now()" style="color: #67C23A;">{{ packageList.find(e => e.id === Number(row.vipFlag))?.text }}</span>
+          <span v-else :style="{'color': !row.vipExpireDate ? '' : '#E6A23C'}">{{ !row.vipExpireDate ? '非会员' : '过期' }}</span>
+          <!-- <span :style="{'color': row.vipExpireDate ? row.vipExpireDate > Date.now() ? '#67C23A' : '#E6A23C' : ''}">
+            {{ row.vipExpireDate ? row.vipExpireDate > Date.now() ? packageList.find(e => e.id === Number(row.vipFlag))?.text : '过期' : '非会员' }}
+          </span> -->
         </template>
       </el-table-column>
       <el-table-column label="会员到期时间" align="center" prop="vipExpireFormatDate" />
@@ -132,7 +160,7 @@
   <PositionTypeForm ref="positionRef" @success="getList" />
 
   <!-- 会员套餐 -->
-  <SetVip ref="vipPackageRef" @success="getList" />
+  <SetVip ref="vipPackageRef" :packageList="packageList" @success="getList" />
 </template>
 
 <script setup lang="ts">
@@ -167,7 +195,9 @@ const queryParams = reactive({
   industryId: undefined,
   financingStatus: undefined,
   scale: undefined,
-  createTime: undefined
+  createTime: undefined,
+  vipFlag: undefined,
+  vipExpireDate: undefined
 })
 const positionRef = ref()
 const queryFormRef = ref() // 搜索的表单
@@ -181,6 +211,14 @@ const getTree = async () => {
 }
 getTree()
 
+// 套餐列表
+const packageList = ref([])
+const getPackList = async () => {
+	const data = await EnterpriseApi.getEnterprisePackageList()
+	packageList.value = data
+}
+getPackList()
+
 /** 查询列表 */
 const getList = async () => {
   loading.value = true

+ 257 - 0
src/views/menduner/system/enterpriseMenu/MenuForm.vue

@@ -0,0 +1,257 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+    >
+      <el-form-item label="上级菜单">
+        <el-tree-select
+          v-model="formData.parentId"
+          :data="menuTree"
+          :default-expanded-keys="[0]"
+          :props="defaultProps"
+          check-strictly
+          node-key="id"
+        />
+      </el-form-item>
+      <el-form-item label="菜单名称" prop="name">
+        <el-input v-model="formData.name" clearable placeholder="请输入菜单名称" />
+      </el-form-item>
+      <el-form-item label="菜单类型" prop="type">
+        <el-radio-group v-model="formData.type">
+          <el-radio-button
+            v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
+            :key="dict.label"
+            :label="dict.value"
+          >
+            {{ dict.label }}
+          </el-radio-button>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item v-if="formData.type !== 3" label="菜单图标">
+        <!-- <IconSelect v-model="formData.icon" clearable /> -->
+        <el-input v-model="formData.icon" clearable placeholder="请输入菜单图标" />
+      </el-form-item>
+      <el-form-item v-if="formData.type !== 3" label="路由地址" prop="path">
+        <template #label>
+          <Tooltip
+            message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
+            title="路由地址"
+          />
+        </template>
+        <el-input v-model="formData.path" clearable placeholder="请输入路由地址" />
+      </el-form-item>
+      <el-form-item v-if="formData.type === 2" label="组件地址" prop="component">
+        <el-input v-model="formData.component" clearable placeholder="例如说:system/user/index" />
+      </el-form-item>
+      <el-form-item v-if="formData.type === 2" label="组件名字" prop="componentName">
+        <el-input v-model="formData.componentName" clearable placeholder="例如说:SystemUser" />
+      </el-form-item>
+      <el-form-item v-if="formData.type !== 1" label="权限标识" prop="permission">
+        <template #label>
+          <Tooltip
+            message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
+            title="权限标识"
+          />
+        </template>
+        <el-input v-model="formData.permission" clearable placeholder="请输入权限标识" />
+      </el-form-item>
+      <el-form-item label="显示排序" prop="sort">
+        <el-input-number v-model="formData.sort" :min="0" clearable controls-position="right" />
+      </el-form-item>
+      <el-form-item label="菜单状态" prop="status">
+        <el-radio-group v-model="formData.status">
+          <el-radio
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :key="dict.label"
+            :label="dict.value"
+          >
+            {{ dict.label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item v-if="formData.type !== 3" label="显示状态" prop="visible">
+        <template #label>
+          <Tooltip message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问" title="显示状态" />
+        </template>
+        <el-radio-group v-model="formData.visible">
+          <el-radio key="true" :label="true" border>显示</el-radio>
+          <el-radio key="false" :label="false" border>隐藏</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item v-if="formData.type !== 3" label="总是显示" prop="alwaysShow">
+        <template #label>
+          <Tooltip
+            message="选择不是时,当该菜单只有一个子菜单时,不展示自己,直接展示子菜单"
+            title="总是显示"
+          />
+        </template>
+        <el-radio-group v-model="formData.alwaysShow">
+          <el-radio key="true" :label="true" border>总是</el-radio>
+          <el-radio key="false" :label="false" border>不是</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item v-if="formData.type === 2" label="缓存状态" prop="keepAlive">
+        <template #label>
+          <Tooltip
+            message="选择缓存时,则会被 `keep-alive` 缓存,必须填写「组件名称」字段"
+            title="缓存状态"
+          />
+        </template>
+        <el-radio-group v-model="formData.keepAlive">
+          <el-radio key="true" :label="true" border>缓存</el-radio>
+          <el-radio key="false" :label="false" border>不缓存</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script lang="ts" setup>
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import * as MenuApi from '@/api/menduner/system/menu'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
+import { CommonStatusEnum, SystemMenuTypeEnum } from '@/utils/constants'
+import { defaultProps, handleTree } from '@/utils/tree'
+
+defineOptions({ name: 'SystemMenuForm' })
+
+const { wsCache } = useCache()
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  id: 0,
+  name: '',
+  permission: '',
+  type: SystemMenuTypeEnum.DIR,
+  sort: Number(undefined),
+  parentId: 0,
+  path: '',
+  icon: '',
+  component: '',
+  componentName: '',
+  status: CommonStatusEnum.ENABLE,
+  visible: true,
+  keepAlive: true,
+  alwaysShow: true
+})
+const formRules = reactive({
+  name: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
+  sort: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
+  path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }],
+  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number, parentId?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  if (parentId) {
+    formData.value.parentId = parentId
+  }
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await MenuApi.getMenu(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+  // 获得菜单列表
+  await getTree()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    if (
+      formData.value.type === SystemMenuTypeEnum.DIR ||
+      formData.value.type === SystemMenuTypeEnum.MENU
+    ) {
+      if (!isExternal(formData.value.path)) {
+        if (formData.value.parentId === 0 && formData.value.path.charAt(0) !== '/') {
+          message.error('路径必须以 / 开头')
+          return
+        } else if (formData.value.parentId !== 0 && formData.value.path.charAt(0) === '/') {
+          message.error('路径不能以 / 开头')
+          return
+        }
+      }
+    }
+    const data = formData.value as unknown as MenuApi.MenuVO
+    if (formType.value === 'create') {
+      await MenuApi.createMenu(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await MenuApi.updateMenu(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+    // 清空,从而触发刷新
+    wsCache.delete(CACHE_KEY.ROLE_ROUTERS)
+  }
+}
+
+/** 获取下拉框[上级菜单]的数据  */
+const menuTree = ref<Tree[]>([]) // 树形结构
+const getTree = async () => {
+  menuTree.value = []
+  const res = await MenuApi.getSimpleMenusList()
+  let menu: Tree = { id: 0, name: '主类目', children: [] }
+  menu.children = handleTree(res)
+  menuTree.value.push(menu)
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: 0,
+    name: '',
+    permission: '',
+    type: SystemMenuTypeEnum.DIR,
+    sort: Number(undefined),
+    parentId: 0,
+    path: '',
+    icon: '',
+    component: '',
+    componentName: '',
+    status: CommonStatusEnum.ENABLE,
+    visible: true,
+    keepAlive: true,
+    alwaysShow: true
+  }
+  formRef.value?.resetFields()
+}
+
+/** 判断 path 是不是外部的 HTTP 等链接 */
+const isExternal = (path: string) => {
+  return /^(https?:|mailto:|tel:)/.test(path)
+}
+</script>

+ 214 - 0
src/views/menduner/system/enterpriseMenu/index.vue

@@ -0,0 +1,214 @@
+<template>
+
+  <!-- 搜索工作栏 -->
+  <ContentWrap>
+    <el-form
+      ref="queryFormRef"
+      :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
+      label-width="68px"
+    >
+      <el-form-item label="菜单名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          class="!w-240px"
+          clearable
+          placeholder="请输入菜单名称"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          class="!w-240px"
+          clearable
+          placeholder="请选择菜单状态"
+        >
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon class="mr-5px" icon="ep:search" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          重置
+        </el-button>
+        <el-button
+          v-hasPermi="['menduner:system:menu:create']"
+          plain
+          type="primary"
+          @click="openForm('create')"
+        >
+          <Icon class="mr-5px" icon="ep:plus" />
+          新增
+        </el-button>
+        <el-button plain type="danger" @click="toggleExpandAll">
+          <Icon class="mr-5px" icon="ep:sort" />
+          展开/折叠
+        </el-button>
+        <!-- <el-button plain @click="refreshMenu">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          刷新菜单缓存
+        </el-button> -->
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table
+      v-if="refreshTable"
+      v-loading="loading"
+      :data="list"
+      :default-expand-all="isExpandAll"
+      row-key="id"
+    >
+      <el-table-column :show-overflow-tooltip="true" label="菜单名称" prop="name" width="250" />
+      <el-table-column align="center" label="图标" prop="icon" width="100">
+        <template #default="scope">
+          <Icon :icon="scope.row.icon" />
+        </template>
+      </el-table-column>
+      <el-table-column label="排序" prop="sort" width="60" />
+      <el-table-column :show-overflow-tooltip="true" label="权限标识" prop="permission" />
+      <el-table-column :show-overflow-tooltip="true" label="组件路径" prop="component" />
+      <el-table-column :show-overflow-tooltip="true" label="组件名称" prop="componentName" />
+      <el-table-column label="状态" prop="status" width="80">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="操作">
+        <template #default="scope">
+          <el-button
+            v-hasPermi="['menduner:system:menu:update']"
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+          >
+            修改
+          </el-button>
+          <el-button
+            v-hasPermi="['menduner:system:menu:create']"
+            link
+            type="primary"
+            @click="openForm('create', undefined, scope.row.id)"
+          >
+            新增
+          </el-button>
+          <el-button
+            v-hasPermi="['menduner:system:menu:delete']"
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <MenuForm ref="formRef" @success="getList" />
+</template>
+<script lang="ts" setup>
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+
+import { handleTree } from '@/utils/tree'
+import * as MenuApi from '@/api/menduner/system/menu'
+import MenuForm from './MenuForm.vue'
+import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
+
+defineOptions({ name: 'MendunerMenu' })
+
+const { wsCache } = useCache()
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const loading = ref(true) // 列表的加载中
+const list = ref<any>([]) // 列表的数据
+const queryParams = reactive({
+  name: undefined,
+  status: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+const isExpandAll = ref(false) // 是否展开,默认全部折叠
+const refreshTable = ref(true) // 重新渲染表格状态
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await MenuApi.getMenuList(queryParams)
+    list.value = handleTree(data)
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number, parentId?: number) => {
+  formRef.value.open(type, id, parentId)
+}
+
+/** 展开/折叠操作 */
+const toggleExpandAll = () => {
+  refreshTable.value = false
+  isExpandAll.value = !isExpandAll.value
+  nextTick(() => {
+    refreshTable.value = true
+  })
+}
+
+/** 刷新菜单缓存按钮操作 */
+const refreshMenu = async () => {
+  try {
+    await message.confirm('即将更新缓存刷新浏览器!', '刷新菜单缓存')
+    // 清空,从而触发刷新
+    wsCache.delete(CACHE_KEY.USER)
+    wsCache.delete(CACHE_KEY.ROLE_ROUTERS)
+    // 刷新浏览器
+    location.reload()
+  } catch {}
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await MenuApi.deleteMenu(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>

+ 9 - 10
src/views/menduner/system/jobFair/manage/details/index.vue

@@ -26,20 +26,18 @@
   <!-- 列表 -->
   <ContentWrap>
     <el-table v-loading="loading" :data="list" :stripe="true">
-      <!-- <el-table-column label="招聘会标题" align="center" prop="jobFair.title">
-        <template #default="scope">
-          <div v-html="scope.row.jobFair.title"></div>
-        </template>
-      </el-table-column> -->
-      <el-table-column label="企业名称" align="center" prop="name">
-        <template #default="scope">{{ formatName(scope.row.name) }}</template>
+      <el-table-column label="企业全称" align="center" prop="name">
+        <template #default="scope">{{ formatName(scope.row.enterprise.name) }}</template>
+      </el-table-column>
+      <el-table-column label="企业别称" align="center" prop="anotherName">
+        <template #default="scope">{{ formatName(scope.row.enterprise.anotherName) }}</template>
       </el-table-column>
       <el-table-column label="操作" align="center">
         <template #default="scope">
           <el-button
             link
             type="primary"
-            @click="handleRemoveWhiteList(scope.row.name)"
+            @click="handleRemoveWhiteList(formatName(scope.row.enterprise.anotherName || scope.row.enterprise.name), scope.row.enterpriseId)"
           >
             移出白名单
           </el-button>
@@ -121,10 +119,11 @@ const handleAdd = () => {
 }
 
 // 移出白名单
-const handleRemoveWhiteList = async (enterpriseName: string) => {
+const handleRemoveWhiteList = async (enterpriseName: string, enterpriseId: string) => {
+  if (!enterpriseId) return message.warning('操作失败,请刷新页面重试')
   try {
     await message.confirm(`确定要将【${enterpriseName}】移出白名单吗?`)
-    await JobFairWhiteApi.removeJobFairWhiteList({ enterpriseName, jobFairId: queryParams.value.jobFairId })
+    await JobFairWhiteApi.removeJobFairWhiteList({ enterpriseIds: enterpriseId, jobFairId: queryParams.value.jobFairId })
     message.success('移出成功')
     getList()
   } catch (err) {}

+ 8 - 18
src/views/menduner/system/jobFair/manage/details/jobFairForm.vue

@@ -7,20 +7,10 @@
       label-width="100px"
       v-loading="formLoading"
     >
-      <el-form-item label="企业名称" prop="enterpriseName">
-        <!-- <el-select v-model="formData.enterpriseName" filterable clearable allow-create default-first-option :reserve-keyword="false" placeholder="请输入企业名称进行查找">
-          <el-option v-for="(item, index) in enterpriseList" :key="index" :label="item.name" :value="item.name" />
-        </el-select> -->
-        <el-select-v2
-          v-model="formData.enterpriseName"
-          :options="enterpriseList"
-          placeholder="请输入企业名称进行查找"
-          allow-create
-          filterable
-          :props="{ label: 'name', value: 'name' }"
-          clearable
-        />
-        <div style="display: flex; color: orange; align-items: center;"><Icon :size="20" icon="ep:warning" class="mr-3px" />提示:此处输入搜索匹配的是企业全称</div>
+      <el-form-item label="企业名称" prop="enterpriseIds">
+        <el-select v-model="formData.enterpriseIds" multiple filterable clearable placeholder="请输入企业全称进行查找">
+          <el-option v-for="(item, index) in enterpriseList" :key="index" :label="item.name" :value="item.id" />
+        </el-select>
       </el-form-item>
     </el-form>
     <template #footer>
@@ -43,11 +33,11 @@ const enterpriseList = ref([])
 const dialogVisible = ref(false) // 弹窗的是否展示
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 const formData = ref({
-  enterpriseName: '',
+  enterpriseIds: [],
   jobFairId: ''
 })
 const formRules = reactive({
-  enterpriseName: [{ required: true, message: '请选择要添加的企业', trigger: 'change' }]
+  enterpriseIds: [{ required: true, message: '请选择要添加的企业', trigger: 'change' }]
 })
 const formRef = ref() // 表单 Ref
 
@@ -77,7 +67,7 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    await JobFairWhiteApi.addJobFairWhiteList(formData.value)
+    await JobFairWhiteApi.addJobFairWhiteList({ jobFairId: formData.value.jobFairId, enterpriseIds: formData.value.enterpriseIds.join(',') })
     message.success('添加成功')
     dialogVisible.value = false
     // 发送操作成功的事件
@@ -90,7 +80,7 @@ const submitForm = async () => {
 /** 重置表单 */
 const resetForm = () => {
   formData.value = {
-    enterpriseName: '',
+    enterpriseIds: [],
     jobFairId: ''
   }
   formRef.value?.resetFields()

+ 178 - 0
src/views/menduner/system/vote/VotingActivityJoinItem.vue

@@ -0,0 +1,178 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="90px">
+			<el-form-item label="投票活动" prop="votingActivityJoinItemCategory">
+				<el-select v-model="queryParams.votingActivityId" filterable placeholder="投票活动" class="!w-240px" @change="handleVotingActivityChange">
+					<el-option v-for="item in votingActivityList" :key="item.votingActivityId" :label="item.activityTitle" :value="item.votingActivityId" />
+				</el-select>
+      </el-form-item>
+      <el-form-item label="候选人名称" prop="votingActivityJoinItemTitle">
+        <el-input v-model="queryParams.votingActivityJoinItemTitle" placeholder="请输入候选人名称" clearable @keyup.enter="handleQuery" class="!w-240px" />
+      </el-form-item>
+			<el-form-item label="候选人类型" prop="votingActivityJoinItemCategory">
+        <el-input v-model="queryParams.votingActivityJoinItemCategory" placeholder="请输入候选人类型" clearable @keyup.enter="handleQuery" class="!w-240px" />
+      </el-form-item>
+			<el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          type="daterange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-240px"
+        />
+      </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', queryParams.votingActivityId)">
+          <Icon icon="ep:plus" class="mr-5px" /> 新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list" :stripe="true">
+			<el-table-column label="序号" align="center" prop="num" />
+			<el-table-column label="候选人封面" align="center" prop="headImg" width="200">
+        <template #default="scope">
+          <el-image v-if="scope.row.headImg" class="h-100px w-200px" :initial-index="0" :src="scope.row.headImg" lazy preview-teleported :preview-src-list="[scope.row.headImg]" fit="scale-down" />
+        </template>
+      </el-table-column>
+      <el-table-column label="候选人名称" align="center" prop="votingActivityJoinItemTitle" />
+      <el-table-column label="候选人类型" align="center" prop="votingActivityJoinItemCategory" />
+      <el-table-column label="票数" align="center" prop="votingActivityJoinItemNum" />
+      <el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="170px" />
+      <el-table-column label="操作" align="center" fixed="right" width="170px">
+        <template #default="scope">
+          <el-button link type="primary" @click="openForm('update', queryParams.votingActivityId, scope.row.votingActivityJoinItemId)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row.votingActivityJoinItemId)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <VotingActivityJoinItemForm ref="formRef" :candidate="candidate" @success="getList" />
+</template>
+
+<script setup lang="ts">
+import { dateFormatter } from '@/utils/formatTime'
+import { VotingActivityApi } from '@/api/menduner/system/vote'
+import VotingActivityJoinItemForm from './VotingActivityJoinItemForm.vue'
+import { useTagsViewStore } from '@/store/modules/tagsView'
+import { ElMessage } from 'element-plus'
+
+/** 专业 列表 */
+defineOptions({ name: 'VotingActivityJoinItem' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+	votingActivityId: undefined,
+	votingActivityJoinItemTitle: undefined,
+	votingActivityJoinItemCategory: undefined,
+	createTime: []
+})
+const queryFormRef = ref() // 搜索的表单
+
+// 候选人列表
+const candidate = ref([])
+const getCandidateList = async (votingActivityId) => {
+  try {
+		const data = await VotingActivityApi.getVotingActivityCategories(votingActivityId)
+		candidate.value = data
+	} catch {}
+}
+
+const votingActivityList = ref([])
+const getVotingActivityList = async () => {
+  try {
+		const { list } = await VotingActivityApi.getVotingActivityPage({ pageNo: 1, pageSize: 100 })
+		votingActivityList.value = list
+	} catch {}
+}
+
+/** 查询列表 */
+const getList = async () => {
+	// 候选人列表
+	getCandidateList(queryParams.votingActivityId)
+  loading.value = true
+  try {
+    const data = await VotingActivityApi.getVotingActivityJoinItemPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+const handleVotingActivityChange = () => {
+	queryParams.pageNo = 1
+	getList()
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, votingActivityId: number, votingActivityJoinItemId?: number) => {
+  formRef.value.open(type, votingActivityId, votingActivityJoinItemId)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await VotingActivityApi.deleteVotingActivityJoinItem(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 初始化 */
+const { currentRoute } = useRouter() // 路由
+const { delView } = useTagsViewStore() // 视图操作
+const route = useRoute()
+const { id } = route.params
+onMounted(() => {
+  if (!id) {
+    ElMessage.warning('参数错误,投票活动id不能为空!')
+    delView(unref(currentRoute))
+    return
+  }
+  queryParams.votingActivityId = id
+  getList()
+	getVotingActivityList()
+})
+</script>

+ 140 - 0
src/views/menduner/system/vote/VotingActivityJoinItemForm.vue

@@ -0,0 +1,140 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible" class="!w-50%">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="150px"
+      v-loading="formLoading"
+      :validate-on-rule-change="false"
+    >
+      <el-form-item label="候选人名称" prop="votingActivityJoinItemTitle">
+        <el-input v-model="formData.votingActivityJoinItemTitle" placeholder="请输入候选人名称" class="!w-240px"/>
+      </el-form-item>
+			<el-form-item label="候选人类型" prop="votingActivityJoinItemCategory">
+				<el-select
+					v-model="formData.votingActivityJoinItemCategory"
+					filterable
+					allow-create
+					default-first-option
+					:reserve-keyword="false"
+					class="!w-240px"
+					placeholder="请输入候选人类型"
+				>
+					<el-option v-for="(item, index) in candidate" :key="index" :label="item" :value="item" />
+				</el-select>
+      </el-form-item>
+			<el-form-item label="候选人封面图" prop="headImg">
+        <UploadImg v-model="formData.headImg" height="150px" width="150px" :fileSize="20" />
+      </el-form-item>
+			<el-form-item label="候选人轮播图" prop="votingActivityJoinItemContent.banners">
+        <UploadImgs v-model="formData.votingActivityJoinItemContent.banners" />
+      </el-form-item>
+      <el-form-item label="候选人介绍" prop="votingActivityJoinItemContent.content">
+        <Editor v-model="formData.votingActivityJoinItemContent.content" height="150px" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+
+<script setup lang="ts">
+import { VotingActivityApi } from '@/api/menduner/system/vote'
+
+defineOptions({ name: 'VotingActivityJoinItemForm' })
+defineProps({
+  area: Array,
+  position: Array,
+	candidate: Array
+})
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+	votingActivityJoinItemId: undefined,
+	votingActivityId: undefined as number | undefined,
+	votingActivityJoinItemTitle: undefined,
+	votingActivityJoinItemCategory: undefined,
+	votingActivityJoinItemContent: {
+		banners: [],
+		content: undefined,
+		video: undefined
+	},
+	headImg: undefined
+})
+
+const formRules = reactive({
+  votingActivityJoinItemTitle: [{ required: true, message: '候选人名称不能为空', trigger: 'blur' }],
+  votingActivityJoinItemCategory: [{ required: true, message: '候选人类型不能为空', trigger: 'change' }],
+  headImg: [{ required: true, message: '候选人封面图不能为空', trigger: 'change' }],
+	'votingActivityJoinItemContent.banners': [{ required: true, message: '候选人轮播图不能为空', trigger: 'change' }],
+	'votingActivityJoinItemContent.content': [{ required: true, message: '候选人介绍不能为空', trigger: 'blur' }],
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async (type: string, votingActivityId: number, votingActivityJoinItemId?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+	formData.value.votingActivityId = votingActivityId
+  // 修改时,设置数据
+  if (votingActivityJoinItemId) {
+    formLoading.value = true
+    try {
+      formData.value = await VotingActivityApi.getVotingActivityJoinItem(votingActivityJoinItemId)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  await formRef.value.validate()
+  // 提交请求
+  formLoading.value = true
+  try {
+    if (formType.value === 'create') {
+      await VotingActivityApi.createVotingActivityJoinItem(formData.value)
+      message.success(t('common.createSuccess'))
+    } else {
+      await VotingActivityApi.updateVotingActivityJoinItem(formData.value)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+		votingActivityJoinItemId: undefined,
+		votingActivityId: undefined,
+		votingActivityJoinItemTitle: undefined,
+		votingActivityJoinItemCategory: undefined,
+		votingActivityJoinItemContent: {
+			banners: [],
+			content: undefined,
+			video: undefined
+		},
+		headImg: undefined
+	}
+  formRef.value?.resetFields()
+}
+</script>

+ 152 - 0
src/views/menduner/system/vote/index.vue

@@ -0,0 +1,152 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+      <el-form-item label="活动名称" prop="activityTitle">
+        <el-input v-model="queryParams.activityTitle" placeholder="请输入活动名称" clearable @keyup.enter="handleQuery" class="!w-240px" />
+      </el-form-item>
+			<el-form-item label="举办机构" prop="activityOrganizer">
+        <el-input v-model="queryParams.activityOrganizer" placeholder="请输入举办机构" clearable @keyup.enter="handleQuery" class="!w-240px" />
+      </el-form-item>
+			<el-form-item label="开始时间" prop="activityStartDate">
+        <el-date-picker v-model="queryParams.activityStartDate" value-format="x" type="date" placeholder="请选择活动开始时间" class="!w-240px" />
+      </el-form-item>
+			<el-form-item label="结束时间" prop="activityEndDate">
+        <el-date-picker v-model="queryParams.activityEndDate" value-format="x" type="date" placeholder="请选择活动结束时间" class="!w-240px" />
+      </el-form-item>
+			<el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          value-format="YYYY-MM-DD HH:mm:ss"
+          type="daterange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-240px"
+        />
+      </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" :stripe="true">
+      <el-table-column label="活动名称" align="center" prop="activityTitle" />
+      <el-table-column label="举办机构" align="center" prop="activityOrganizer" />
+			<el-table-column label="开始时间" align="center" prop="activityStartDate" :formatter="dateFormatter2" width="110px" />
+			<el-table-column label="结束时间" align="center" prop="activityEndDate" :formatter="dateFormatter2" width="110px" />
+			<el-table-column label="投票频率" align="center">
+        <template #default="{ row }">
+          {{ row.votingRule.dateType === 'day' ? '每天' : row.votingRule.dateType === 'month' ? '每月' : row.votingRule.dateType === 'year' ? '每年' : '' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="指定频率内可投票数" align="center" prop="votingRule.num" />
+      <el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="170px" />
+      <el-table-column label="操作" align="center" fixed="right" width="170px">
+        <template #default="scope">
+          <el-button link type="success" @click="openCandidateDetail(scope.row.votingActivityId)">候选人</el-button>
+          <el-button link type="primary" @click="openForm('update', scope.row.votingActivityId)">编辑</el-button>
+          <el-button link type="danger" @click="handleDelete(scope.row.votingActivityId)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <VotingActivityForm ref="formRef" @success="getList" />
+</template>
+
+<script setup lang="ts">
+// import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
+import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
+import { VotingActivityApi } from '@/api/menduner/system/vote'
+import VotingActivityForm from './votingActivityForm.vue'
+
+/** 专业 列表 */
+defineOptions({ name: 'Vote' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+	activityTitle: undefined,
+	activityStartDate: undefined,
+	activityEndDate: undefined,
+	activityOrganizer: undefined,
+	createTime: []
+})
+const queryFormRef = ref() // 搜索的表单
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await VotingActivityApi.getVotingActivityPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 跳转候选人详情 */
+const { push } = useRouter()
+const openCandidateDetail = (id) => {
+  push({ name: 'VotingActivityJoinItem', params: { id } })
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await VotingActivityApi.deleteVotingActivity(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>

+ 180 - 0
src/views/menduner/system/vote/votingActivityForm.vue

@@ -0,0 +1,180 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible" class="!w-50%">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="150px"
+      v-loading="formLoading"
+      :validate-on-rule-change="false"
+    >
+      <el-form-item label="活动名称" prop="activityTitle">
+        <el-input v-model="formData.activityTitle" placeholder="请输入活动名称" class="!w-240px"/>
+      </el-form-item>
+			<el-form-item label="举办机构" prop="activityOrganizer">
+        <el-input v-model="formData.activityOrganizer" placeholder="请输入举办机构名称" class="!w-240px"/>
+      </el-form-item>
+      <el-form-item label="开始时间" prop="activityStartDate">
+        <el-date-picker v-model="formData.activityStartDate" :disabledDate="disabledDates" value-format="x" type="date" placeholder="请选择活动开始时间" class="!w-240px" />
+      </el-form-item>
+			<el-form-item label="结束时间" prop="activityEndDate">
+        <el-date-picker v-model="formData.activityEndDate" :disabledDate="disabledEndDates" value-format="x" type="date" placeholder="请选择活动结束时间" class="!w-240px" />
+      </el-form-item>
+			<el-form-item label="投票频率" prop="votingRule.dateType">
+				<el-radio-group v-model="formData.votingRule.dateType">
+					<el-radio value="day">每天</el-radio>
+					<el-radio value="month">每月</el-radio>
+					<el-radio value="year">每年</el-radio>
+				</el-radio-group>
+			</el-form-item>
+			<el-form-item label="指定频率内可投票数" prop="votingRule.num">
+				<el-input-number v-model="formData.votingRule.num" :min="1" :step="1" />
+			</el-form-item>
+			<el-form-item label="活动图片" prop="headImg">
+        <UploadImg v-model="formData.headImg" height="150px" width="150px" :fileSize="20" />
+      </el-form-item>
+			<el-form-item label="活动背景图" prop="activityContent.backgroundImg">
+        <UploadImg v-model="formData.activityContent.backgroundImg" height="150px" width="150px" :fileSize="20" />
+      </el-form-item>
+      <el-form-item label="活动内容" prop="activityContent.content">
+        <Editor v-model="formData.activityContent.content" height="150px" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+
+<script setup lang="ts">
+import { VotingActivityApi } from '@/api/menduner/system/vote'
+
+defineOptions({ name: 'VotingActivityForm' })
+defineProps({
+  area: Array,
+  position: Array
+})
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+	votingActivityId: undefined,
+	activityTitle: undefined,
+	activityStartDate: undefined,
+	activityEndDate: undefined,
+	activityContent: {
+		backgroundImg: undefined,
+		content: undefined
+	},
+	activityOrganizer: undefined,
+	votingRule: {
+		dateType: 'day', // 投票频率:day - 日,month - 月,year - 年
+		num: 1 // 指定频率内最多投票次数
+	},
+	headImg: undefined
+})
+
+const formRules = reactive({
+  activityTitle: [{ required: true, message: '活动名称不能为空', trigger: 'blur' }],
+  activityStartDate: [{ required: true, message: '活动开始时间', trigger: 'change' }],
+  activityEndDate: [{ required: true, message: '活动结束时间', trigger: 'change' }],
+  activityOrganizer: [{ required: true, message: '举办机构不能为空', trigger: 'blur' }],
+  headImg: [{ required: true, message: '活动图片不能为空', trigger: 'change' }],
+	'activityContent.backgroundImg': [{ required: true, message: '活动背景图不能为空', trigger: 'change' }],
+	'activityContent.content': [{ required: true, message: '活动内容不能为空', trigger: 'blur' }],
+	'votingRule.dateType': [{ required: true, message: '投票频率不能为空', trigger: 'change' }],
+	'votingRule.num': [{ required: true, message: '投票频率不能为空', trigger: 'change' }],
+})
+const formRef = ref() // 表单 Ref
+
+// 过去的日期不可选
+const disabledDates = (date) => {
+  const currentDate = new Date()
+  currentDate.setDate(currentDate.getDate() - 1)
+  return date.getTime() < currentDate.getTime()
+}
+
+// 根据选择的开始时间,结束时间不可选
+const disabledEndDates = (date) => {
+  const currentDate = new Date()
+  currentDate.setDate(currentDate.getDate() - 1)
+
+  const activityStartDate = formData.value.activityStartDate
+  // 没有选择开始时间时默认过去的时间不可选
+  if (!activityStartDate) {
+    return date.getTime() < currentDate.getTime()
+  }
+
+  // 有开始时间则根据开始时间设置不可选范围
+  const startDate = new Date(activityStartDate)
+  startDate.setDate(startDate.getDate() + 1)
+  return date.getTime() < startDate.getTime()
+}
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await VotingActivityApi.getVotingActivity(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  await formRef.value.validate()
+  // 提交请求
+  formLoading.value = true
+  try {
+    if (formType.value === 'create') {
+      await VotingActivityApi.createVotingActivity(formData.value)
+      message.success(t('common.createSuccess'))
+    } else {
+      await VotingActivityApi.updateVotingActivity(formData.value)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+		votingActivityId: undefined,
+		activityTitle: undefined,
+		activityStartDate: undefined,
+		activityEndDate: undefined,
+		activityContent: {
+			backgroundImg: undefined,
+			content: undefined
+		},
+		activityOrganizer: undefined,
+		votingRule: {
+			dateType: 'day',
+			num: 1
+		},
+		headImg: undefined
+	}
+  formRef.value?.resetFields()
+}
+</script>

+ 2 - 2
src/views/menduner/system/web/PreferredGroup.vue

@@ -27,7 +27,7 @@
         <UploadImg v-model="formData.logo" height="150px" width="150px" />
       </el-form-item>
       <el-form-item label="顶部轮播图" prop="carousel" :rules="[{ required: true, message: '请上传企业轮播图', trigger: 'change'}]">
-        <UploadImgs v-model="formData.carousel" />
+        <UploadImgs v-model="formData.carousel" :limit="20" />
       </el-form-item>
 			<el-form-item label="简介标题" prop="introduce.title" :rules="[{ required: true, message: '请输入简介标题', trigger: 'blur'}]">
         <el-input v-model="formData.introduce.title" />
@@ -36,7 +36,7 @@
         <Editor v-model:modelValue="formData.introduce.describe" />
       </el-form-item>
 			<el-form-item label="简介小图" prop="introduce.thumbnail">
-        <UploadImgs v-model="formData.introduce.thumbnail" />
+        <UploadImgs v-model="formData.introduce.thumbnail" :limit="20" />
       </el-form-item>
 			<el-form-item label="简介大图" prop="introduce.bigPicture.url">
         <UploadImg v-model="formData.introduce.bigPicture.url" height="150px" width="300px" />

+ 8 - 5
src/views/menduner/system/web/WebContentForm.vue

@@ -36,6 +36,9 @@
       <el-form-item label="标题" prop="title">
         <el-input v-model="formData.title" placeholder="请填写" />
       </el-form-item>
+      <el-form-item label="排序" prop="sort">
+        <el-input v-model="formData.sort" placeholder="请填写" />
+      </el-form-item>
       <el-form-item label="状态" prop="status" required>
         <el-radio-group v-model="formData.status">
           <el-radio value="0">激活</el-radio>
@@ -65,7 +68,7 @@ const formData = ref({
   link: '',
   title: '',
   status: '0',
-  // sort: 0,
+  sort: 0,
   url: ''
 })
 const maxWidth = ref(0)
@@ -125,8 +128,8 @@ const open = async (type: string, key: string, title: string, mark?: string) =>
     // 编辑
     if (mark) {
       editId.value = mark
-      const { img: url, link, title, status } = query.value[key].find(e => e.mark === mark)
-      formData.value = { url, link, title, status }
+      const { img: url, link, title, status, sort } = query.value[key].find(e => e.mark === mark)
+      formData.value = { url, link, title, status, sort }
     }
   } finally {
     formLoading.value = false
@@ -145,7 +148,7 @@ const submitForm = async () => {
   await formRef.value.validate()
   
   const mark = new Date().getTime().toString()
-  const obj = { img: formData.value.url, link: formData.value.link, mark, title: formData.value.title, status: formData.value.status }
+  const obj = { img: formData.value.url, link: formData.value.link, mark, title: formData.value.title, status: formData.value.status, sort: formData.value.sort }
   if (formType.value === 'add') {
     query.value[currentKey.value] = query.value[currentKey.value] ? [...query.value[currentKey.value], obj] : [obj]
   } else {
@@ -190,7 +193,7 @@ const resetForm = () => {
     title: '',
     status: '0',
     url: '',
-    // sort: 0
+    sort: 0
   }
   formRef.value?.resetFields()
 }

+ 2 - 1
src/views/menduner/system/web/index.vue

@@ -18,8 +18,9 @@
     </div>
 
     <el-table v-if="tab !== 9" v-loading="loading" :data="info[tabList[tab].key]" :stripe="true">
+      <el-table-column label="排序" align="center" prop="sort" />
       <el-table-column label="标题" align="center" prop="title" />
-      <el-table-column label="图片" align="center" prop="img">
+      <el-table-column label="图片" align="center" prop="img" width="200">
         <template #default="scope">
           <el-image v-if="scope.row.img" class="h-150px w-200px" :initial-index="info[tabList[tab].key].findIndex(e => e.mark === scope.row.mark)" :src="scope.row.img" lazy preview-teleported :preview-src-list="info[tabList[tab].key].map(e => e.img)" fit="scale-down" />
         </template>