feat:优化社团入会
This commit is contained in:
+15
-3
@@ -1,6 +1,7 @@
|
||||
package com.budwk.app.zhgh.club.controller.apply;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
@@ -47,8 +48,19 @@ public class ClubUserClubListController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开移动端社团列表,共用电脑端的社团查询及入退会状态。
|
||||
*
|
||||
* @return 无返回值,由模板渲染社团列表页面
|
||||
*/
|
||||
@At("/h5")
|
||||
@Ok("beetl:/platform/zhghh5/club/list/index.html")
|
||||
@SaCheckPermission(value = {"h5.club.join.list", "h5.club.join.apply"}, mode = SaMode.OR)
|
||||
public void h5Index() {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("club.join.list")
|
||||
@SaCheckPermission(value = {"club.join.list", "h5.club.join.list", "h5.club.join.apply"}, mode = SaMode.OR)
|
||||
public Result pageData(@Valid PageForm pageForm, Boolean hasJoin, String clubName, String clubType) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("dismiss", "=", false);
|
||||
@@ -71,7 +83,7 @@ public class ClubUserClubListController {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("club.join.list")
|
||||
@SaCheckPermission(value = {"club.join.list", "h5.club.join.list", "h5.club.join.apply"}, mode = SaMode.OR)
|
||||
public Result getCount() {
|
||||
// 获取所有社团
|
||||
List<SysClub> list = sysClubService.query();
|
||||
@@ -96,7 +108,7 @@ public class ClubUserClubListController {
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("club.join.list")
|
||||
@SaCheckPermission(value = {"club.join.list", "h5.club.join.list", "h5.club.join.apply"}, mode = SaMode.OR)
|
||||
public Result findOne(@Valid String id) {
|
||||
ClubRegisterVo clubRegisterVo = sysClubService.findOne(id);
|
||||
return Result.success(clubRegisterVo);
|
||||
|
||||
+14
-2
@@ -74,10 +74,22 @@ public class ClubUserJoinApplyController {
|
||||
public void index() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端首次入会先选择社团,已选社团或已有申请继续进入申请表单。
|
||||
*
|
||||
* @param clubId 已选择的社团ID
|
||||
* @param bizId 已有入会申请ID
|
||||
* @param taskId 重新提交申请时的流程任务ID
|
||||
* @return 无申请上下文时跳转社团列表,否则渲染原申请页面
|
||||
*/
|
||||
@At("/h5")
|
||||
@SaCheckPermission("h5.club.join.apply")
|
||||
@Ok("beetl:/platform/zhghh5/club/apply/index.html")
|
||||
public void h5Index() {
|
||||
@Ok("re")
|
||||
public String h5Index(@Param("clubId") String clubId, @Param("bizId") String bizId, @Param("taskId") String taskId) {
|
||||
if (StrUtil.isBlank(clubId) && StrUtil.isBlank(bizId) && StrUtil.isBlank(taskId)) {
|
||||
return ">>:/platform/club/join/list/h5";
|
||||
}
|
||||
return "beetl:/platform/zhghh5/club/apply/index.html";
|
||||
}
|
||||
|
||||
@At
|
||||
|
||||
@@ -2,12 +2,17 @@ package com.budwk.app.zhgh.club.controller.common;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.annotation.SaMode;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.bpm.enums.BpmProcessInstanceStatusEnum;
|
||||
import com.budwk.app.bpm.enums.BpmProcessTaskStatusEnum;
|
||||
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
|
||||
import com.budwk.app.sys.enums.SysFileEngineTypeEnum;
|
||||
import com.budwk.app.sys.services.SysFileService;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.club.model.ClubUser;
|
||||
@@ -19,10 +24,17 @@ import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.AdaptBy;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.POST;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
import org.nutz.mvc.upload.UploadAdaptor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/club/common")
|
||||
@@ -36,6 +48,36 @@ public class ClubCommonController {
|
||||
|
||||
@Inject
|
||||
private SysClubService sysClubService;
|
||||
@Inject
|
||||
private SysFileService sysFileService;
|
||||
|
||||
/**
|
||||
* 上传社团缩略图,限制图片大小并复用系统文件服务校验图片内容及保存文件。
|
||||
*
|
||||
* @param file 用户上传的 JPG、PNG 或 GIF 图片,大小不得超过 2MB
|
||||
* @return 成功时返回图片下载地址,文件无效或超过限制时返回错误提示
|
||||
*/
|
||||
@At
|
||||
@POST
|
||||
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
|
||||
@SaCheckPermission(value = {"club.register.apply", "club.infoManage.manage"}, mode = SaMode.OR)
|
||||
public Result uploadThumbnail(@Param("file") TempFile file) {
|
||||
if (file == null || StrUtil.isBlank(file.getSubmittedFileName())) {
|
||||
return Result.error("请选择社团缩略图");
|
||||
}
|
||||
if (file.getSize() > 2L * 1024 * 1024) {
|
||||
return Result.error("社团缩略图大小不能超过2MB");
|
||||
}
|
||||
String suffix = FileUtil.extName(file.getSubmittedFileName()).toLowerCase(Locale.ROOT);
|
||||
if (!Set.of("jpg", "jpeg", "png", "gif").contains(suffix)) {
|
||||
return Result.error("社团缩略图仅支持JPG、PNG、GIF图片");
|
||||
}
|
||||
try {
|
||||
return Result.success().addData(sysFileService.uploadReturnUrl(SysFileEngineTypeEnum.MINIO.getValue(), file));
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckLogin
|
||||
|
||||
@@ -37,6 +37,11 @@ public class SysClub extends BaseModel {
|
||||
@NotEmpty(message = "社团名称不能为空")
|
||||
private String clubName;
|
||||
|
||||
@Column
|
||||
@Comment("社团缩略图")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 1000)
|
||||
private String thumbnail;
|
||||
|
||||
@Column
|
||||
@Comment("社团编码")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
|
||||
@@ -112,11 +112,14 @@ public class SysClubServiceImpl extends BaseServiceImpl<SysClub> implements SysC
|
||||
if (StrUtil.isBlank(clubId)) {
|
||||
return new ClubRegisterVo();
|
||||
}
|
||||
// 与社团列表保持一致,按社团成员角色汇总会长、秘书长姓名,避免多名任职人员导致详情记录重复。
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
club.*,
|
||||
d.NAME AS typeName,
|
||||
u.username concatPersonName,
|
||||
(select GROUP_CONCAT(username) from sys_user where id in (select userId from club_user u where u.clubId=club.id and JSON_CONTAINS(u.roleCode, '"CLUB_PRESIDENT"'))) as clubLeader,
|
||||
(select GROUP_CONCAT(username) from sys_user where id in (select userId from club_user u where u.clubId=club.id and JSON_CONTAINS(u.roleCode, '"CLUB_SECRETARY"'))) as clubSecretary,
|
||||
( SELECT count( 1 ) FROM club_user scu WHERE scu.clubId = club.id ) currentPeopleNum,
|
||||
(select GROUP_CONCAT(username) from sys_user where id in (select sponsorId from sys_club_sponsor where clubId = club.id)) as sponsorName,
|
||||
(select GROUP_CONCAT(sponsorId) from sys_club_sponsor where clubId = club.id) as sponsor
|
||||
|
||||
@@ -19,4 +19,10 @@ public class ClubRegisterVo extends SysClub {
|
||||
private String sponsorName;
|
||||
private Integer currentPeopleNum;
|
||||
private String sponsor;
|
||||
|
||||
@ApiModelProperty("会长")
|
||||
private String clubLeader;
|
||||
|
||||
@ApiModelProperty("秘书长")
|
||||
private String clubSecretary;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
-- 社团缩略图保存系统文件下载地址;保留可空以兼容未上传图片的历史社团。
|
||||
-- 字段存在时跳过,避免重复执行迁移时报错。
|
||||
SET @club_thumbnail_column_count = (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'sys_club'
|
||||
AND COLUMN_NAME = 'thumbnail'
|
||||
);
|
||||
|
||||
SET @club_thumbnail_ddl = IF(
|
||||
@club_thumbnail_column_count = 0,
|
||||
'ALTER TABLE sys_club ADD COLUMN thumbnail VARCHAR(1000) NULL COMMENT ''社团缩略图'' AFTER clubName',
|
||||
'SELECT 1'
|
||||
);
|
||||
|
||||
PREPARE club_thumbnail_stmt FROM @club_thumbnail_ddl;
|
||||
EXECUTE club_thumbnail_stmt;
|
||||
DEALLOCATE PREPARE club_thumbnail_stmt;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
@@ -8,6 +8,12 @@ const REGISTER_INFO_COMPONENT = {
|
||||
<el-link type="primary" @click="openChart">点击查看流程图</el-link>
|
||||
</div>
|
||||
<el-descriptions :column="3" border class="table_fixed">
|
||||
<el-descriptions-item label="社团缩略图" :span="3">
|
||||
<el-image v-if="viewData.thumbnail" :src="viewData.thumbnail" :preview-src-list="[viewData.thumbnail]" fit="contain" style="width: 180px; height: 120px">
|
||||
<span slot="error">暂无图片</span>
|
||||
</el-image>
|
||||
<span v-else>暂无图片</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="社团名称">
|
||||
{{viewData.clubName}}
|
||||
</el-descriptions-item>
|
||||
|
||||
@@ -2,9 +2,19 @@
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<style>
|
||||
.club-join-apply-page .custom-card > .card-header { padding: 12px 24px; }
|
||||
.club-join-apply-page .club-apply-back-bar { display: flex; align-items: center; }
|
||||
.club-join-apply-page .club-apply-back-bar .el-button { padding: 0; font-size: 16px; }
|
||||
.club-join-apply-page .club-apply-title { margin-bottom: 20px; }
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak class="club-join-apply-page">
|
||||
<custom-card>
|
||||
<snaker-start slot="header" label="文体社团会员申请" define_key="XHRH"></snaker-start>
|
||||
<div slot="header" class="club-apply-back-bar">
|
||||
<el-button type="text" icon="el-icon-back" @click="backToClubList">返回</el-button>
|
||||
</div>
|
||||
<snaker-start class="club-apply-title" label="文体社团会员申请" define_key="XHRH"></snaker-start>
|
||||
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" label-suffix=":" class="flow-task-form">
|
||||
<el-descriptions border class="descriptions-form">
|
||||
<el-descriptions-item label="社团" :span="3">
|
||||
@@ -156,6 +166,9 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
backToClubList() {
|
||||
commonUtil.pjaxPush('/platform/club/join/list');
|
||||
},
|
||||
onSave() {
|
||||
this.$confirm("您确定保存吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
@@ -224,6 +237,16 @@ layout("/layouts/platform.html"){
|
||||
this.$axios.post("/platform/club/common/listClub").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.clubOptions = res.data
|
||||
// 从卡片进入新申请时预选社团,编辑已有申请仍使用原记录。
|
||||
const clubId = GetQueryString("clubId")
|
||||
if (!this.bizId && clubId) {
|
||||
const club = this.clubOptions.find(item => item.id === clubId)
|
||||
if (club) {
|
||||
this.$set(this.formData, "clubId", club.id)
|
||||
this.$set(this.formData, "clubName", club.clubName)
|
||||
this.checkApplyClub(club.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -11,9 +11,36 @@ layout("/layouts/platform.html"){
|
||||
.el-tabs__header {
|
||||
margin: 0;
|
||||
}
|
||||
.club-directory .club-card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 20px; }
|
||||
.club-directory .club-card { display: flex; flex-direction: column; min-width: 0; overflow: hidden; border: 1px solid #e4edf8; border-radius: 10px; background: #fff; box-shadow: 0 7px 20px rgba(55, 85, 126, .08); }
|
||||
.club-directory .club-card-main { flex: 1; min-width: 0; }
|
||||
.club-directory .club-card-cover { width: 100%; height: 180px; overflow: hidden; background: #edf4fc; }
|
||||
.club-directory .club-card-cover .el-image { width: 100%; height: 100%; }
|
||||
.club-directory .club-image-empty { display: flex; flex-direction: column; gap: 8px; height: 100%; align-items: center; justify-content: center; color: #8da1ba; font-size: 13px; background: #edf4fc; }
|
||||
.club-directory .club-image-empty i { font-size: 34px; }
|
||||
.club-directory .club-card-info { min-width: 0; padding: 14px 16px; }
|
||||
.club-directory .club-card-name { margin: 0; color: #303846; font-size: 16px; font-weight: 400; line-height: 24px; overflow-wrap: anywhere; }
|
||||
.club-directory .club-card-meta { margin: 5px 0 0; color: #98a2b2; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }
|
||||
.club-directory .club-card-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; padding-top: 10px; border-top: 1px solid #edf1f6; }
|
||||
.club-directory .club-card-tag { display: inline-flex; align-items: center; padding: 3px 8px; border-radius: 4px; background: #edf5ff; color: #397de0; font-size: 12px; line-height: 18px; }
|
||||
.club-directory .club-card-tag-label { margin-right: 4px; color: #8aa7cf; }
|
||||
.club-directory .club-card-actions { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 0 14px; margin: 0 16px; border-top: 1px solid #edf1f6; }
|
||||
.club-directory .club-card-actions .el-button { height: 32px; margin: 0; padding: 0 14px; border: 0; border-radius: 16px; font-size: 13px; }
|
||||
.club-directory .club-card-actions .el-button--primary { width: 108px; color: #fff; background: #1989fa; box-shadow: 0 5px 12px rgba(25, 137, 250, .18); }
|
||||
.club-directory .club-card-actions .el-button--primary.is-plain { width: auto; color: #397de0; background: #edf5ff; box-shadow: none; }
|
||||
.club-directory .club-card-actions .el-button--danger { width: 108px; color: #fff; background: #ee0a24; box-shadow: 0 5px 12px rgba(238, 10, 36, .18); }
|
||||
.club-directory .club-card-sort { display: flex; justify-content: flex-end; margin-bottom: 16px; }
|
||||
.club-directory .club-card-sort .el-select { width: 168px; }
|
||||
.club-directory .club-detail-cover { width: 160px; max-width: 100%; height: 110px; overflow: hidden; border-radius: 8px; background: #f8fafc; }
|
||||
.club-directory .club-detail-cover .el-image { display: block; width: 100%; height: 100%; border-radius: 8px; }
|
||||
@media (max-width: 640px) {
|
||||
.club-directory .club-card-grid { grid-template-columns: minmax(0, 1fr); }
|
||||
.club-directory .club-card-cover { height: 150px; }
|
||||
.club-directory .club-detail-cover { width: 120px; height: 90px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<div id="app" v-cloak class="club-directory">
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
@@ -38,29 +65,56 @@ layout("/layouts/platform.html"){
|
||||
<el-radio-button :label="true">{{'已加入社团' + hasJoinCount + '(退会)'}}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" row-key="id" style="width: 100%">
|
||||
<el-table-column :index="indexMethod" label="序号" type="index" width="80px"></el-table-column>
|
||||
<el-table-column label="社团编码" sortable prop="clubCode"></el-table-column>
|
||||
<el-table-column label="社团名称" prop="clubName" show-overflow-tooltip width="200px"></el-table-column>
|
||||
<el-table-column label="成立时间" prop="foundTime"></el-table-column>
|
||||
<el-table-column label="会长" prop="clubLeader" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="秘书长" prop="clubSecretary" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column sortable label="当前人数" show-overflow-tooltip prop="currentPeopleNum"></el-table-column>
|
||||
<el-table-column label="操作" width="250px">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="primary" @click="openDetail(scope.row)">查看</el-button>
|
||||
<el-button size="mini" v-if="hasJoin === false" type="primary" @click="doSubmit(scope.row)">申请加入</el-button>
|
||||
<el-button size="mini" v-else type="danger" @click="onExit(scope.row)">申请退出</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="club-card-sort">
|
||||
<el-select v-model="clubSort" size="small" aria-label="社团排序" @change="changeClubSort">
|
||||
<el-option label="社团编码升序" value="clubCode:ascending"></el-option>
|
||||
<el-option label="社团编码降序" value="clubCode:descending"></el-option>
|
||||
<el-option label="当前人数升序" value="currentPeopleNum:ascending"></el-option>
|
||||
<el-option label="当前人数降序" value="currentPeopleNum:descending"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="club-card-grid" v-loading="tableLoading">
|
||||
<article v-for="row in tableData" :key="row.id" class="club-card">
|
||||
<div class="club-card-main">
|
||||
<div class="club-card-cover">
|
||||
<el-image v-if="row.thumbnail" :src="row.thumbnail" :alt="row.clubName + '缩略图'" fit="cover" :preview-src-list="[row.thumbnail]">
|
||||
<div slot="error" class="club-image-empty"><i class="el-icon-picture-outline"></i><span>暂无图片</span></div>
|
||||
</el-image>
|
||||
<div v-else class="club-image-empty"><i class="el-icon-picture-outline"></i><span>暂无图片</span></div>
|
||||
</div>
|
||||
<div class="club-card-info">
|
||||
<h3 class="club-card-name">{{row.clubName}}</h3>
|
||||
<p class="club-card-meta">成立时间:{{row.foundTime || '--'}}</p>
|
||||
<div class="club-card-tags">
|
||||
<span class="club-card-tag"><span class="club-card-tag-label">会长</span>{{row.clubLeader || '--'}}</span>
|
||||
<span class="club-card-tag"><span class="club-card-tag-label">社团类型</span>{{formatClubType(row.clubType)}}</span>
|
||||
<span class="club-card-tag"><span class="club-card-tag-label">会费标准</span>{{row.due == null || row.due === '' ? '--' : row.due + ' 元/年'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="club-card-actions">
|
||||
<el-button size="mini" type="primary" plain icon="el-icon-view" @click="openDetail(row)">查看</el-button>
|
||||
<el-button size="mini" v-if="hasJoin === false" type="primary" icon="el-icon-right" @click="doSubmit(row)">申请加入</el-button>
|
||||
<el-button size="mini" v-else type="danger" icon="el-icon-switch-button" @click="onExit(row)">申请退出</el-button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<el-empty v-if="!tableLoading && !tableData.length" description="暂无社团"></el-empty>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #view>
|
||||
<el-descriptions class="margin-top" title="" :column="2" border>
|
||||
<el-descriptions-item span="2">
|
||||
<el-descriptions-item label="社团图片">
|
||||
<div class="club-detail-cover">
|
||||
<el-image v-if="viewData.thumbnail" :src="viewData.thumbnail" :alt="viewData.clubName + '缩略图'" fit="contain" :preview-src-list="[viewData.thumbnail]">
|
||||
<div slot="error" class="club-image-empty">暂无图片</div>
|
||||
</el-image>
|
||||
<div v-else class="club-image-empty">暂无图片</div>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item>
|
||||
<template slot="label">社团名称</template>
|
||||
<div v-html="viewData.clubName"></div>
|
||||
</el-descriptions-item>
|
||||
@@ -103,6 +157,7 @@ layout("/layouts/platform.html"){
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
dicts: ['CLUB_REGISTER_TYPE'],
|
||||
mixins: [initTableMixins],
|
||||
components: {},
|
||||
data() {
|
||||
@@ -111,10 +166,21 @@ layout("/layouts/platform.html"){
|
||||
hasJoin: false,
|
||||
hasJoinCount: 0,
|
||||
noJoinCount: 0,
|
||||
clubSort: 'clubCode:ascending',
|
||||
clickRow: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatClubType(value) {
|
||||
const option = (this.dict.type.CLUB_REGISTER_TYPE || []).find((item) => String(item.code) === String(value));
|
||||
return option ? option.name : '--';
|
||||
},
|
||||
changeClubSort(value) {
|
||||
const [prop, order] = value.split(':')
|
||||
this.pageForm.pageOrderName = prop
|
||||
this.pageForm.pageOrderBy = order
|
||||
this.doSearch()
|
||||
},
|
||||
onExit(row) {
|
||||
this.$confirm("您确定要申请退出" + row.clubName + "吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
@@ -162,9 +228,14 @@ layout("/layouts/platform.html"){
|
||||
},
|
||||
pageData() {
|
||||
this.pageForm.hasJoin = this.hasJoin
|
||||
this.$axios.post("/platform/club/join/list/pageData", this.pageForm).then((res) => {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
this.tableLoading = true
|
||||
return this.$axios.post("/platform/club/join/list/pageData", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
}).finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -58,6 +58,12 @@ layout("/layouts/platform.html"){
|
||||
<el-tabs tab-position="top" v-model="activeName">
|
||||
<el-tab-pane label="社团基础信息" name="1">
|
||||
<el-descriptions style="margin-top: 20px;" class="margin-top" title="" :column="2" border>
|
||||
<el-descriptions-item label="社团缩略图" :span="2">
|
||||
<el-image v-if="viewData.thumbnail" :src="viewData.thumbnail" :preview-src-list="[viewData.thumbnail]" fit="contain" style="width: 180px; height: 120px">
|
||||
<span slot="error">暂无图片</span>
|
||||
</el-image>
|
||||
<span v-else>暂无图片</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="社团名称">{{viewData.clubName}}</el-descriptions-item>
|
||||
<el-descriptions-item label="成立时间">{{viewData.foundTime}}</el-descriptions-item>
|
||||
<el-descriptions-item label="社团编码">{{viewData.clubCode}}</el-descriptions-item>
|
||||
|
||||
@@ -79,6 +79,20 @@ const CLUB_FORM_TEMPLATE = {
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="社团缩略图" prop="thumbnail">
|
||||
<file-upload
|
||||
:value.sync="formData.thumbnail"
|
||||
:upload_number="1"
|
||||
:upload_size="2 * 1024 * 1024"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
upload_dynamic_return_url_api="/platform/club/common/uploadThumbnail"
|
||||
accept=".jpg,.jpeg,.png,.gif"
|
||||
></file-upload>
|
||||
<div style="color: #909399; font-size: 12px">支持 JPG、PNG、GIF 图片,大小不超过 2MB</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="社团介绍及宗旨" prop="introduce">
|
||||
<text-editor v-model="formData.introduce"></text-editor>
|
||||
</el-form-item>
|
||||
@@ -156,7 +170,8 @@ const CLUB_FORM_TEMPLATE = {
|
||||
return {
|
||||
formData: {
|
||||
clubName: "",
|
||||
clubType: ""
|
||||
clubType: "",
|
||||
thumbnail: ""
|
||||
},
|
||||
formRules: {
|
||||
clubName: [{ required: true, message: "请填写社团名称", trigger: ["blur", "change"] }],
|
||||
|
||||
@@ -29,7 +29,7 @@ layout("/layouts/platform_h5.html"){
|
||||
.club-apply-actions__agree .van-checkbox__icon { margin-top: 2px; }
|
||||
.club-apply-actions__agree .van-checkbox__label { color: #65778e; font-size: 12px; line-height: 18px; }
|
||||
.club-apply-actions__buttons { display: flex; gap: 10px; }
|
||||
.club-apply-actions .van-button { flex: 1; height: 40px; border-radius: 8px; font-size: 14px; font-weight: 500; }
|
||||
.club-apply-actions .van-button { flex: 1; height: 40px; border-radius: 20px; font-size: 14px; font-weight: 500; }
|
||||
.club-apply-actions .van-button--info { background: var(--club-primary); border-color: var(--club-primary); }
|
||||
.club-apply-actions .van-button--info.van-button--plain { color: var(--club-primary); background: #fff; }
|
||||
</style>
|
||||
@@ -41,7 +41,7 @@ layout("/layouts/platform_h5.html"){
|
||||
<section class="club-apply-section">
|
||||
<h2 class="club-apply-section__title">基本信息</h2>
|
||||
<van-cell-group :border="false">
|
||||
<van-field :rules="[{ required: true, message: '请选择社团' }]" v-model="formData.clubName" label="社团" placeholder="请选择社团" required is-link readonly name="clubName" @click="openClubPopup"></van-field>
|
||||
<van-field :rules="[{ required: true, message: '请从社团列表选择社团后进入申请' }]" v-model="formData.clubName" label="社团" placeholder="从社团列表带入" required readonly name="clubName"></van-field>
|
||||
<van-field label="姓名" :rules="[{ required: true }]" v-model="formData.userName" readonly required name="userName"></van-field>
|
||||
<van-field label="工号" :rules="[{ required: true }]" v-model="formData.loginName" readonly required name="loginName"></van-field>
|
||||
<van-field label="性别" :rules="[{ required: true }]" v-model="formData.sex" readonly required></van-field>
|
||||
@@ -98,10 +98,6 @@ layout("/layouts/platform_h5.html"){
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-popup v-model="showClubPopup" position="bottom" get-container="#app" @click-overlay="closeClubPopup">
|
||||
<van-picker show-toolbar :columns="clubOptions.map((item) => item.clubName)" @confirm="onClubConfirm" @cancel="closeClubPopup"></van-picker>
|
||||
</van-popup>
|
||||
|
||||
<van-dialog :value="confirmVisible" title="提示" :message="confirmMessage" show-cancel-button @confirm="confirmPendingAction" @cancel="closeConfirm"></van-dialog>
|
||||
</div>
|
||||
|
||||
@@ -118,7 +114,6 @@ layout("/layouts/platform_h5.html"){
|
||||
clubId: "", clubName: "", userName: "", loginName: "", sex: "", birthday: "", mobile: "", email: "", unionName: "", unitName: "", position: "", technicalTitle: "", education: "", academicDegree: "", sameTimeJoinOtherClubSituation: "", awardsExperience: "", avatar: "", signature: ""
|
||||
},
|
||||
clubOptions: [],
|
||||
showClubPopup: false,
|
||||
isAgree: false,
|
||||
formLoading: false,
|
||||
confirmVisible: false,
|
||||
@@ -136,7 +131,6 @@ layout("/layouts/platform_h5.html"){
|
||||
if (manager) {
|
||||
manager.ensureRegistered('club-apply');
|
||||
this.historyLayerUnsubscribe = manager.subscribe((layers) => {
|
||||
this.$set(this, 'showClubPopup', layers.includes('club-apply-club-picker'));
|
||||
this.$set(this, 'confirmVisible', layers.includes('club-apply-confirm'));
|
||||
});
|
||||
}
|
||||
@@ -161,25 +155,6 @@ layout("/layouts/platform_h5.html"){
|
||||
}
|
||||
historyBack();
|
||||
},
|
||||
openClubPopup() {
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) {
|
||||
manager.open('club-apply-club-picker');
|
||||
return;
|
||||
}
|
||||
this.$set(this, 'showClubPopup', true);
|
||||
},
|
||||
closeClubPopup(afterClose) {
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) {
|
||||
manager.close('club-apply-club-picker', afterClose);
|
||||
return;
|
||||
}
|
||||
this.$set(this, 'showClubPopup', false);
|
||||
if (afterClose) {
|
||||
afterClose();
|
||||
}
|
||||
},
|
||||
openConfirm(action) {
|
||||
this.$set(this, 'pendingAction', action);
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
@@ -271,19 +246,20 @@ layout("/layouts/platform_h5.html"){
|
||||
}
|
||||
});
|
||||
},
|
||||
onClubConfirm(value, index) {
|
||||
const club = this.clubOptions[index];
|
||||
if (club) {
|
||||
this.$set(this.formData, 'clubId', club.id);
|
||||
this.$set(this.formData, 'clubName', value);
|
||||
this.checkApplyClub(club.id);
|
||||
}
|
||||
this.closeClubPopup();
|
||||
},
|
||||
listClub() {
|
||||
this.$axios.post('/platform/club/common/listClub').then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, 'clubOptions', res.data || []);
|
||||
// 从社团卡片进入新申请时预选社团,编辑已有申请仍使用原记录。
|
||||
const clubId = GetQueryString('clubId');
|
||||
if (!this.bizId && clubId) {
|
||||
const club = this.clubOptions.find((item) => item.id === clubId);
|
||||
if (club) {
|
||||
this.$set(this.formData, 'clubId', club.id);
|
||||
this.$set(this.formData, 'clubName', club.clubName);
|
||||
this.checkApplyClub(club.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
<!--#include('../common/clubUserJoin.css'){}#-->
|
||||
.club-directory-page { min-height: 100vh; padding-bottom: calc(24px + env(safe-area-inset-bottom)); box-sizing: border-box; background: #f3f7fd; }
|
||||
.club-directory-page .van-nav-bar { background: #fff; }
|
||||
.club-directory-page .van-nav-bar__title { color: #1f2937; font-size: 16px; }
|
||||
.club-directory-page .van-nav-bar .van-icon, .club-directory-page .van-nav-bar__text { color: #397fbd; }
|
||||
.club-directory-content { padding: 12px 12px 0; }
|
||||
.club-directory-hero { position: relative; height: 134px; overflow: hidden; border-radius: 10px; color: #fff; background: #247fe8; box-shadow: 0 8px 20px rgba(51, 117, 219, .18); }
|
||||
.club-directory-hero__image { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; object-position: center; }
|
||||
.club-directory-hero__copy { position: relative; z-index: 1; display: flex; width: 64%; height: 100%; padding-left: 16px; flex-direction: column; justify-content: center; box-sizing: border-box; background: linear-gradient(90deg, rgba(22, 112, 232, .98) 0%, rgba(22, 112, 232, .88) 52%, rgba(22, 112, 232, 0) 100%); }
|
||||
.club-directory-hero__title { font-size: 25px; line-height: 34px; font-weight: 700; letter-spacing: 1px; text-shadow: 0 2px 6px rgba(17, 86, 182, .18); }
|
||||
.club-directory-hero__subtitle { margin-top: 4px; font-size: 13px; line-height: 20px; letter-spacing: .5px; opacity: .95; }
|
||||
.club-directory-sticky { padding: 10px 0 1px; background: #f3f7fd; }
|
||||
.club-directory-page .van-sticky--fixed .club-directory-sticky { padding-right: 12px; padding-left: 12px; box-sizing: border-box; }
|
||||
.club-directory-search { margin: 0 0 8px; padding: 0; overflow: hidden; border-radius: 12px; background: #fff; box-shadow: 0 5px 16px rgba(43, 73, 112, .1); }
|
||||
.club-directory-search .van-search__content { height: 42px; padding-left: 12px; align-items: center; border-radius: 12px; background: #fff; }
|
||||
.club-directory-search .van-field__control { color: #263548; font-size: 14px; }
|
||||
.club-directory-search .van-field__control::placeholder { color: #a7b0bf; }
|
||||
.club-directory-tabs { margin: 0 0 10px; overflow: hidden; border-radius: 10px; background: #fff; box-shadow: 0 5px 16px rgba(43, 73, 112, .08); }
|
||||
.club-directory-tabs .van-tab { color: #66758a; font-size: 13px; }
|
||||
.club-directory-tabs .van-tab--active { color: #1989fa; }
|
||||
.club-directory-list { padding: 0; }
|
||||
.club-directory-page .table-list-container { margin-top: 0; }
|
||||
.club-directory-page .table-list-container .table-list-item { margin-bottom: 10px; padding: 0; border: 0; border-radius: 10px; background: #fff; box-shadow: 0 7px 20px rgba(55, 85, 126, .08); }
|
||||
.club-directory-card { width: 100%; }
|
||||
.club-directory-cover { width: 100%; height: 150px; overflow: hidden; background: #edf4fc; }
|
||||
.club-directory-cover .van-image, .club-directory-detail-cover .van-image { display: block; width: 100%; height: 100%; }
|
||||
.club-directory-image-empty { display: flex; width: 100%; height: 100%; flex-direction: column; row-gap: 8px; align-items: center; justify-content: center; background: #edf4fc; color: #8da1ba; font-size: 13px; }
|
||||
.club-directory-image-empty .van-icon { font-size: 34px; }
|
||||
.club-directory-info { min-width: 0; padding: 12px 14px 14px; }
|
||||
.club-directory-name { margin: 0; color: #303846; font-size: 14px; font-weight: 400; line-height: 21px; overflow-wrap: anywhere; }
|
||||
.club-directory-meta { margin: 4px 0 0; color: #98a2b2; font-size: 12px; line-height: 17px; }
|
||||
.club-directory-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 9px; }
|
||||
.club-directory-info .club-directory-tags { margin-top: 10px; padding-top: 9px; border-top: 1px solid #edf1f6; }
|
||||
.club-directory-tag { display: inline-flex; padding: 3px 8px; align-items: center; border-radius: 4px; color: #397de0; background: #edf5ff; font-size: 11px; line-height: 16px; }
|
||||
.club-directory-tag-label { margin-right: 4px; color: #8aa7cf; }
|
||||
.club-directory-page .table-list-container .table-list-item .item-actions { margin: 0 14px; padding: 10px 0 14px; border-top-color: #edf1f6; }
|
||||
.club-directory-page .item-actions .van-button { height: 28px; padding: 0 12px; border-radius: 14px; font-size: 12px; }
|
||||
.club-directory-page .item-actions .club-directory-card-button { height: 32px; padding: 0 14px; border: 0; border-radius: 16px; color: #397de0; background-color: #edf5ff; font-size: 13px; }
|
||||
.club-directory-page .item-actions .club-directory-card-button i { margin-right: 5px; font-size: 13px; }
|
||||
.club-directory-page .item-actions .club-directory-card-button--join { width: 108px; color: #fff; background-color: #1989fa; box-shadow: 0 5px 12px rgba(25, 137, 250, .18); }
|
||||
.club-directory-page .item-actions .club-directory-card-button--exit { width: 108px; color: #fff; background-color: #ee0a24; box-shadow: 0 5px 12px rgba(238, 10, 36, .18); }
|
||||
.club-directory-loading { padding: 50px 0; text-align: center; }
|
||||
.club-directory-detail-cover { height: 230px; flex: none; background: #f2f7fc; }
|
||||
.club-directory-page .club-user-join-detail__content { position: relative; margin-top: -16px; padding: 0 0 18px; }
|
||||
.club-directory-detail-heading { padding: 16px 14px 12px; }
|
||||
.club-directory-detail-heading .club-directory-name { margin-bottom: 6px; font-size: 18px; font-weight: 600; }
|
||||
.club-directory-detail-meta { display: flex; align-items: center; gap: 5px; margin-top: 6px; color: #8b9bb0; font-size: 12px; line-height: 20px; }
|
||||
.club-directory-detail-meta .van-icon { color: #1989fa; font-size: 14px; }
|
||||
.club-directory-detail-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0 14px; padding: 16px 0; border-top: 1px solid #eef2f7; }
|
||||
.club-directory-detail-stat { min-width: 0; padding: 0 8px; text-align: center; }
|
||||
.club-directory-detail-stat + .club-directory-detail-stat { border-left: 1px solid #eef2f7; }
|
||||
.club-directory-detail-stat span { display: block; margin-bottom: 6px; color: #8b9bb0; font-size: 12px; line-height: 18px; }
|
||||
.club-directory-detail-stat strong { display: block; color: #40526a; font-size: 14px; font-weight: 400; line-height: 20px; overflow-wrap: anywhere; }
|
||||
.club-directory-page .club-user-join-card { border-radius: 16px; }
|
||||
.club-directory-page .club-user-join-info-card .van-cell__value { font-size: 13px; }
|
||||
.club-directory-page .club-user-join-info-long-cell img { max-width: 100%; height: auto; }
|
||||
.club-directory-material-preview { position: relative; height: 72px; overflow: hidden; }
|
||||
.club-directory-material-preview.is-expanded { height: auto; overflow: visible; }
|
||||
.club-directory-material-preview.is-truncated::after { position: absolute; right: 0; bottom: 0; padding-left: 18px; background: linear-gradient(90deg, transparent, #fff 18px); content: '…'; }
|
||||
.club-directory-material-content { display: flow-root; overflow-wrap: anywhere; }
|
||||
.club-directory-material-rich { font-size: 16px; line-height: 1.6; }
|
||||
.club-directory-material-rich, .club-directory-material-rich * { max-width: 100% !important; box-sizing: border-box; }
|
||||
.club-directory-material-rich p, .club-directory-material-rich h1, .club-directory-material-rich h2, .club-directory-material-rich h3, .club-directory-material-rich h4, .club-directory-material-rich h5, .club-directory-material-rich h6 { margin: 0; }
|
||||
.club-directory-material-rich img, .club-directory-material-rich video { height: auto !important; }
|
||||
.club-directory-material-rich table { width: 100% !important; table-layout: fixed; }
|
||||
.club-directory-material-toggle { height: 24px; margin-top: 4px; text-align: right; }
|
||||
.club-directory-material-toggle button { padding: 0; border: 0; background: transparent; color: #1989fa; font-size: 12px; line-height: 24px; cursor: pointer; }
|
||||
.club-directory-detail-actions { display: flex; flex: none; gap: 12px; padding: 10px 15px calc(10px + env(safe-area-inset-bottom)); background: #fff; border-top: 1px solid #edf2f8; }
|
||||
.club-directory-detail-actions .van-button { flex: 1; height: 40px; border-radius: 20px; }
|
||||
.club-directory-detail-actions .van-button--info { border-color: #1989fa; background-color: #1989fa; }
|
||||
.club-directory-detail-actions .van-button--info.van-button--plain { color: #1989fa; background-color: #fff; }
|
||||
.club-directory-detail-actions .van-button--danger { color: #fff; border-color: #ee0a24; background-color: #ee0a24; }
|
||||
.club-directory-detail-actions .van-button--danger i { margin-right: 5px; }
|
||||
.club-directory-page .empty-state { padding: 0; }
|
||||
.club-directory-empty { display: flex; min-height: 260px; padding: 26px 18px 32px; align-items: center; justify-content: center; flex-direction: column; box-sizing: border-box; text-align: center; }
|
||||
.club-directory-empty img { display: block; width: 82%; max-width: 280px; height: auto; border-radius: 12px; object-fit: contain; }
|
||||
.club-directory-empty__title { margin-top: 14px; color: #50627a; font-size: 15px; line-height: 22px; font-weight: 600; }
|
||||
.club-directory-empty__hint { margin-top: 5px; color: #9aa7b8; font-size: 12px; line-height: 18px; }
|
||||
.club-directory-page .van-list__finished-text, .club-directory-page .van-list__loading { color: #9aa7b8; font-size: 13px; line-height: 44px; }
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak class="club-directory-page">
|
||||
<van-nav-bar title="社团列表" left-text="返回" left-arrow fixed placeholder @click-left="onNavBack"></van-nav-bar>
|
||||
<div class="club-directory-content">
|
||||
<section class="club-directory-hero">
|
||||
<img class="club-directory-hero__image" src="/assets/mobile/img/club/club-hero-v5.png" alt="社团入会插画">
|
||||
<div class="club-directory-hero__copy">
|
||||
<div class="club-directory-hero__title">社团入会</div>
|
||||
<div class="club-directory-hero__subtitle">以兴趣相聚 · 让生活精彩</div>
|
||||
</div>
|
||||
</section>
|
||||
<van-sticky :offset-top="46">
|
||||
<div class="club-directory-sticky">
|
||||
<van-search v-model="pageForm.clubName" class="club-directory-search" placeholder="请输入社团名称搜索" shape="round" clearable @search="doSearch" @clear="doSearch"></van-search>
|
||||
<van-tabs v-model="activeTab" class="club-directory-tabs" color="#1989fa" @change="joinChange">
|
||||
<van-tab :title="'可加入社团(' + noJoinCount + ')'" name="available"></van-tab>
|
||||
<van-tab :title="'已加入社团(' + hasJoinCount + ')'" name="joined"></van-tab>
|
||||
</van-tabs>
|
||||
</div>
|
||||
</van-sticky>
|
||||
<main class="club-directory-list">
|
||||
<table-list api="/platform/club/join/list/pageData" :page_form.sync="pageForm" ref="tableListRef" :show_loading_overlay="false" @ready="doSearch" @loading-change="onLoadingChange">
|
||||
<template #header><div></div></template>
|
||||
<template v-slot="{row}">
|
||||
<div v-if="row" class="club-directory-card">
|
||||
<div class="club-directory-cover">
|
||||
<van-image v-if="row.thumbnail" :src="row.thumbnail" :alt="row.clubName + '缩略图'" fit="cover"><template #error><div class="club-directory-image-empty"><van-icon name="photo-o"></van-icon><span>暂无图片</span></div></template></van-image>
|
||||
<div v-else class="club-directory-image-empty"><van-icon name="photo-o"></van-icon><span>暂无图片</span></div>
|
||||
</div>
|
||||
<div class="club-directory-info">
|
||||
<h2 class="club-directory-name">{{ row.clubName }}</h2>
|
||||
<p class="club-directory-meta">成立时间:{{ row.foundTime || '--' }}</p>
|
||||
<div class="club-directory-tags">
|
||||
<span class="club-directory-tag"><span class="club-directory-tag-label">会长</span>{{ row.clubLeader || '--' }}</span>
|
||||
<span class="club-directory-tag"><span class="club-directory-tag-label">社团类型</span>{{ formatClubType(row.clubType) }}</span>
|
||||
<span class="club-directory-tag"><span class="club-directory-tag-label">会费标准</span>{{ row.due == null || row.due === '' ? '--' : row.due + ' 元/年' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions="{row}">
|
||||
<template v-if="row">
|
||||
<van-button class="club-directory-card-button" native-type="button" size="small" type="default" @click="onView(row)"><i class="fa fa-eye"></i>查看</van-button>
|
||||
<van-button v-if="!pageForm.hasJoin" class="club-directory-card-button club-directory-card-button--join" native-type="button" size="small" type="default" :loading="formLoading" @click="onJoin(row)"><i class="fa fa-sign-in"></i>申请加入</van-button>
|
||||
<van-button v-else class="club-directory-card-button club-directory-card-button--exit" native-type="button" size="small" type="danger" :disabled="formLoading" @click="onExit(row)"><i class="fa fa-sign-out"></i>申请退出</van-button>
|
||||
</template>
|
||||
</template>
|
||||
<template #empty>
|
||||
<div v-if="listLoading" class="club-directory-loading"><van-loading size="24px" vertical>加载中</van-loading></div>
|
||||
<div v-else class="club-directory-empty"><img src="/assets/mobile/img/club/club-approval-empty.png" alt="社团空状态插画"><div class="club-directory-empty__title">暂无社团</div><div class="club-directory-empty__hint">当前筛选条件下暂时没有可展示的社团</div></div>
|
||||
</template>
|
||||
</table-list>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<van-popup v-model="clubDetailVisible" position="right" :style="{ width: '100%', height: '100%' }" get-container="#app" class="club-user-join-popup" :close-on-click-overlay="false" @opened="observeDetailMaterials">
|
||||
<div class="club-user-join-detail">
|
||||
<van-nav-bar title="社团详情" left-text="返回" left-arrow fixed @click-left="closeClubDetail"></van-nav-bar>
|
||||
<div class="club-user-join-detail__scroll">
|
||||
<div class="club-directory-detail-cover">
|
||||
<van-image v-if="clubDetail.thumbnail" :src="clubDetail.thumbnail" :alt="clubDetail.clubName + '缩略图'" fit="cover"><template #error><span class="club-directory-image-empty">暂无图片</span></template></van-image>
|
||||
<div v-else class="club-directory-image-empty">暂无图片</div>
|
||||
</div>
|
||||
<main class="club-user-join-detail__content">
|
||||
<section class="club-user-join-card club-user-join-info-card">
|
||||
<div class="club-directory-detail-heading">
|
||||
<h2 class="club-directory-name">{{ clubDetail.clubName }}</h2>
|
||||
<div class="club-directory-detail-meta">成立时间:{{ clubDetail.foundTime || '--' }}</span></div>
|
||||
</div>
|
||||
<div class="club-directory-detail-stats">
|
||||
<div class="club-directory-detail-stat"><span>社团类型</span><strong>{{ formatClubType(clubDetail.clubType) }}</strong></div>
|
||||
<div class="club-directory-detail-stat"><span>当前人数</span><strong>{{ clubDetail.currentPeopleNum || 0 }} 人</strong></div>
|
||||
<div class="club-directory-detail-stat"><span>会费标准</span><strong>{{ clubDetail.due == null || clubDetail.due === '' ? '--' : clubDetail.due + ' 元/年' }}</strong></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="club-user-join-card club-user-join-info-card">
|
||||
<h3 class="club-user-join-card__title">人员及联系信息</h3>
|
||||
<van-cell-group :border="false">
|
||||
<van-cell title="会长">{{ clubDetail.clubLeader || '--' }}</van-cell>
|
||||
<van-cell title="秘书长">{{ clubDetail.clubSecretary || '--' }}</van-cell>
|
||||
<van-cell title="发起人">{{ clubDetail.sponsorName || '--' }}</van-cell>
|
||||
<van-cell title="报名联系人">{{ clubDetail.concatPersonName || '--' }}</van-cell>
|
||||
<van-cell title="联系人电话">{{ clubDetail.concatPersonMobile || '--' }}</van-cell>
|
||||
<van-cell title="联系人邮箱">{{ clubDetail.concatPersonEmail || '--' }}</van-cell>
|
||||
</van-cell-group>
|
||||
</section>
|
||||
<section class="club-user-join-card club-user-join-info-card">
|
||||
<h3 class="club-user-join-card__title">社团信息</h3>
|
||||
<van-cell-group :border="false">
|
||||
<van-cell v-for="section in detailSections" :key="section.key" :title="section.title" class="club-user-join-info-long-cell">
|
||||
<div class="club-directory-material-preview" :class="{ 'is-expanded': expandedMaterials[section.key], 'is-truncated': overflowingMaterials[section.key] && !expandedMaterials[section.key] }">
|
||||
<div ref="detailMaterials" :data-material-key="section.key" class="club-directory-material-content">
|
||||
<template v-if="section.files">
|
||||
<file-preview v-if="clubDetail[section.key] && clubDetail[section.key].length" :files="clubDetail[section.key]" complete_result></file-preview><span v-else>暂无</span>
|
||||
</template>
|
||||
<div v-else-if="clubDetail[section.key]" class="club-directory-material-rich" v-html="clubDetail[section.key]" v-mobile-rich-text="clubDetail[section.key]"></div><span v-else>暂无</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="club-directory-material-toggle">
|
||||
<button v-if="overflowingMaterials[section.key]" type="button" :aria-expanded="!!expandedMaterials[section.key]" @click="$set(expandedMaterials, section.key, !expandedMaterials[section.key])">{{ expandedMaterials[section.key] ? '收起' : '展开' }}<van-icon :name="expandedMaterials[section.key] ? 'arrow-up' : 'arrow-down'"></van-icon></button>
|
||||
</div>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
<div class="club-directory-detail-actions">
|
||||
<van-button native-type="button" type="info" plain @click="closeClubDetail">返回列表</van-button>
|
||||
<van-button v-if="!pageForm.hasJoin" native-type="button" type="info" :loading="formLoading" @click="onJoin(clubDetail)">申请加入</van-button>
|
||||
<van-button v-else native-type="button" type="danger" :disabled="formLoading" @click="onExit(clubDetail)"><i class="fa fa-sign-out"></i>申请退出</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-dialog :value="confirmVisible" title="提示" :message="confirmMessage" show-cancel-button :confirm-button-loading="formLoading" @confirm="confirmExit" @cancel="closeConfirm"></van-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
// 仅适配预览字号,保留原始富文本;先读取全部字号,避免父元素缩放后影响子元素的相对字号。
|
||||
function adaptClubMobileRichText(element, binding) {
|
||||
if (element._clubMobileRichText === binding.value) return;
|
||||
element.style.removeProperty('font-size');
|
||||
element.style.removeProperty('line-height');
|
||||
const sizes = [element, ...element.querySelectorAll('*')].map((node) => {
|
||||
const style = window.getComputedStyle(node);
|
||||
const fontSize = parseFloat(style.fontSize) || 16;
|
||||
const lineHeight = parseFloat(style.lineHeight) / fontSize || 1.6;
|
||||
return {node, fontSize, lineHeight};
|
||||
});
|
||||
sizes.forEach(({node, fontSize, lineHeight}) => {
|
||||
// 正文 16px 映射为 14px,大字号适度压缩层级,限制在移动端可读的 10~32px 范围。
|
||||
const mobileSize = Math.min(32, Math.max(10, 14 * Math.pow(fontSize / 16, 0.75)));
|
||||
node.style.setProperty('font-size', mobileSize.toFixed(2) + 'px', 'important');
|
||||
node.style.setProperty('line-height', (mobileSize * Math.min(1.8, Math.max(1.3, lineHeight))).toFixed(2) + 'px', 'important');
|
||||
});
|
||||
element._clubMobileRichText = binding.value;
|
||||
}
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
dicts: ['CLUB_REGISTER_TYPE'],
|
||||
directives: {
|
||||
'mobile-rich-text': {inserted: adaptClubMobileRichText, componentUpdated: adaptClubMobileRichText}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0, clubName: '', hasJoin: false, pageOrderName: 'clubCode', pageOrderBy: 'ascending'},
|
||||
activeTab: 'available', hasJoinCount: 0, noJoinCount: 0, listLoading: true, formLoading: false,
|
||||
detailSections: [{key: 'introduce', title: '社团介绍'}, {key: 'purpose', title: '社团宗旨'}, {key: 'rulesFile', title: '社团章程', files: true}, {key: 'yearPlanFile', title: '年度活动计划', files: true}],
|
||||
expandedMaterials: {}, overflowingMaterials: {},
|
||||
clubDetailVisible: false, clubDetail: {}, confirmVisible: false, currentRow: null, historyLayerUnsubscribe: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
confirmMessage() { return this.currentRow ? '您确定要申请退出“' + this.currentRow.clubName + '”吗?' : '您确定要申请退出该社团吗?'; }
|
||||
},
|
||||
created() {
|
||||
// 详情和退会确认分别入栈,手机返回时只关闭当前弹层。
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) {
|
||||
manager.ensureRegistered('club-directory');
|
||||
this.historyLayerUnsubscribe = manager.subscribe((layers) => {
|
||||
this.clubDetailVisible = layers.includes('club-directory-detail');
|
||||
this.confirmVisible = layers.includes('club-directory-confirm');
|
||||
});
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.detailMaterialObserver) this.detailMaterialObserver.disconnect();
|
||||
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe();
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) manager.unregister('club-directory');
|
||||
},
|
||||
methods: {
|
||||
observeDetailMaterials() {
|
||||
if (this.detailMaterialObserver) this.detailMaterialObserver.disconnect();
|
||||
this.$nextTick(() => {
|
||||
// 富文本图片和附件异步渲染后高度会变化,按实际高度决定是否显示展开入口。
|
||||
this.detailMaterialObserver = new ResizeObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
this.$set(this.overflowingMaterials, entry.target.dataset.materialKey, entry.target.scrollHeight > 72);
|
||||
});
|
||||
});
|
||||
(this.$refs.detailMaterials || []).forEach((element) => this.detailMaterialObserver.observe(element));
|
||||
});
|
||||
},
|
||||
formatClubType(value) {
|
||||
const option = (this.dict.type.CLUB_REGISTER_TYPE || []).find((item) => String(item.code) === String(value));
|
||||
return option ? option.name : '--';
|
||||
},
|
||||
onNavBack() { const manager = window.h5HistoryLayerManager; if (manager && manager.stack.length) { manager.close(); return; } historyBack(); },
|
||||
onLoadingChange(state) { this.listLoading = state.loading; },
|
||||
joinChange(name) { this.$set(this.pageForm, 'hasJoin', name === 'joined'); this.doSearch(); },
|
||||
doSearch() {
|
||||
this.$nextTick(() => {
|
||||
this.$set(this.pageForm, 'pageNumber', 1);
|
||||
this.$set(this.pageForm, 'totalCount', 0);
|
||||
this.$refs.tableListRef.doSearch();
|
||||
});
|
||||
this.$axios.post('/platform/club/join/list/getCount').then((res) => {
|
||||
if (res.code === 0) { this.hasJoinCount = res.data.hasJoinCount; this.noJoinCount = res.data.noJoinCount; }
|
||||
});
|
||||
},
|
||||
onView(row) {
|
||||
this.$axios.post('/platform/club/join/list/findOne', {id: row.id}).then((res) => {
|
||||
if (res.code !== 0) { this.$toast(res.msg); return; }
|
||||
this.clubDetail = res.data;
|
||||
this.expandedMaterials = {};
|
||||
this.overflowingMaterials = {};
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) { manager.open('club-directory-detail'); return; }
|
||||
this.clubDetailVisible = true;
|
||||
});
|
||||
},
|
||||
closeClubDetail() {
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) { manager.close('club-directory-detail'); return; }
|
||||
this.clubDetailVisible = false;
|
||||
},
|
||||
onJoin(row) {
|
||||
if (this.formLoading) return;
|
||||
this.formLoading = true;
|
||||
this.$axios.post('/platform/club/join/apply/checkApplyClub', {clubId: row.id}).then((res) => {
|
||||
if (res.code === 0) this.goToPage('/platform/club/join/apply/h5?clubId=' + encodeURIComponent(row.id));
|
||||
else this.$toast(res.msg);
|
||||
}).finally(() => { this.formLoading = false; });
|
||||
},
|
||||
onExit(row) {
|
||||
if (this.formLoading) return;
|
||||
this.currentRow = row;
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) { manager.open('club-directory-confirm'); return; }
|
||||
this.confirmVisible = true;
|
||||
},
|
||||
closeConfirm(afterClose) {
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) { manager.close('club-directory-confirm', afterClose); return; }
|
||||
this.confirmVisible = false;
|
||||
if (typeof afterClose === 'function') afterClose();
|
||||
},
|
||||
confirmExit() {
|
||||
if (this.formLoading || !this.currentRow) return;
|
||||
const row = this.currentRow;
|
||||
this.formLoading = true;
|
||||
this.closeConfirm(() => {
|
||||
// 列表返回 clubId,详情返回社团 id;统一按退会接口要求传递 clubId。
|
||||
this.$axios.post('/platform/club/join/apply/submit', {data: JSON.stringify({clubId: row.clubId || row.id}), mode: false}).then((res) => {
|
||||
this.$toast(res.msg);
|
||||
if (res.code === 0) this.goToPage('/platform/club/join/mine/h5');
|
||||
}).finally(() => { this.formLoading = false; });
|
||||
});
|
||||
},
|
||||
goToPage(url) {
|
||||
const manager = window.h5HistoryLayerManager;
|
||||
if (manager) { manager.closeAllAndNavigate(url); return; }
|
||||
this.$pjaxReplace(url);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -70,6 +70,10 @@ layout("/layouts/platform_h5.html"){
|
||||
<section class="club-user-join-card club-user-join-info-card">
|
||||
<h3 class="club-user-join-card__title">社团基础信息</h3>
|
||||
<van-cell-group :border="false">
|
||||
<van-cell title="社团缩略图" class="club-user-join-info-long-cell">
|
||||
<van-image v-if="clubDetail.thumbnail" :src="clubDetail.thumbnail" width="100%" height="180" fit="contain"><template #error>暂无图片</template></van-image>
|
||||
<span v-else>暂无图片</span>
|
||||
</van-cell>
|
||||
<van-cell title="社团名称">{{ clubDetail.clubName || '--' }}</van-cell>
|
||||
<van-cell title="成立时间">{{ clubDetail.foundTime || '--' }}</van-cell>
|
||||
<van-cell title="社团编码">{{ clubDetail.clubCode || '--' }}</van-cell>
|
||||
|
||||
Reference in New Issue
Block a user