feat:优化社团入会

This commit is contained in:
2026-10-17 14:17:41 +08:00
parent 453a8d24f1
commit 3ffa1454b1
16 changed files with 614 additions and 65 deletions
@@ -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);
@@ -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>