整改提案

整改慰问增加骨架屏
整改作品征集
整改困难帮扶
This commit is contained in:
2026-08-30 17:04:37 +08:00
parent 17e8379903
commit 58921d9bb2
30 changed files with 6316 additions and 1769 deletions
@@ -70,7 +70,7 @@ public class FlowCommonService {
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "userName", SecurityUtil.getUserUsername());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "loginName", SecurityUtil.getUserLoginname());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitId", SecurityUtil.getUnitId());
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitName", Optional.of(sysUnit).map(Sys_unit::getName).orElse(""));
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unitName", Optional.ofNullable(sysUnit).map(Sys_unit::getName).orElse(""));
args.put(FlowConst.TASK_FORM_DATA_PREFIX + "unionId", SecurityUtil.getUnionId());
if (ObjectUtil.equals(submitType, ProcessSubmitTypeEnum.ROLLBACK.getCode())) {
@@ -11,7 +11,7 @@ import org.nutz.mvc.annotation.Ok;
public class ActivityWorksCollectionNewController {
@At("")
@Ok("beetl:/platform/zhgh/activity/workscollection/manage/index.html")
@Ok("beetl:/platform/zhgh/activity/workscollection/new/index.html")
@SaCheckPermission(value = {"activity.workscollection.manage", "activity.workscollection.new"}, mode = SaMode.OR)
public void index() {
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.activity.workscollection.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
@@ -19,6 +20,7 @@ import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -56,16 +58,21 @@ public class ActivityWorksCollectionUploadController {
/**
* @param pageForm 分页
* @param activityId 活动id
* @param subjectId 作品类型
* @return
* @param subjectId 主题类型主键
* @param worksId 作品类型主键
* @param year 活动开始时间所在年度
* @param activityType 活动状态:1 全部、2 征集中、3 已结束
* @return Resultdata 为当前登录人的作品分页数据
*/
@At
@SaCheckPermission("activity.workscollection.upload")
public Result pageData(@Valid PageForm pageForm, String activityId, String subjectId, String worksId, Long year) {
public Result pageData(@Valid PageForm pageForm, String activityId, String subjectId, String worksId,
Long year, Integer activityType) {
Sql sql = Sqls.create("""
select
up.*,
co.NAME AS activityName,
co.cover AS cover,
co.startDateTime AS activityStartDateTime,
co.endDateTime AS activityEndDateTime,
su.typeName AS subjectName,
@@ -83,6 +90,19 @@ public class ActivityWorksCollectionUploadController {
cnd.andEX("up.activityId", "=", activityId);
cnd.andEX("up.subjectId", "=", subjectId);
cnd.andEX("up.worksId", "=", worksId);
// searchKeyword 支持按作品名称或所属活动名称搜索,空值不参与筛选。
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
String keyword = "%" + pageForm.getSearchKeyword().trim() + "%";
SqlExpressionGroup keywordGroup = Cnd.exps("up.name", "like", keyword)
.or("co.name", "like", keyword);
cnd.and(keywordGroup);
}
// activityType1 或空值查询全部,2 查询征集中,3 查询已结束。
if (activityType != null && activityType == 2) {
cnd.and(new Static("now() >= co.startDateTime and now() < co.endDateTime"));
} else if (activityType != null && activityType == 3) {
cnd.and(new Static("now() >= co.endDateTime"));
}
cnd.desc("up.createdAt");
sql.setCondition(cnd);
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
@@ -2,6 +2,7 @@ package com.budwk.app.zhgh.activity.workscollection.controller.h5;
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;
import com.budwk.app.base.result.Result;
@@ -64,7 +65,11 @@ public class H5ActivityWorksUploadCollectionController {
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("YEAR(awc.startDateTime)", "=", year);
//查询报名中
// searchKeyword 为活动名称关键字,空值不参与筛选;接口仍返回分页对象。
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
cnd.and("awc.name", "like", "%" + pageForm.getSearchKeyword().trim() + "%");
}
// activityType1 全部、2 征集中、3 已结束、4 即将开始。
if (activityType == 2) {
cnd.and(new Static("now() > awc.startDateTime and now() < awc.endDateTime"));
}//查询已结束的
@@ -99,7 +99,6 @@ public class DifficultHelpMineController {
difficult_help_info info
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
Binary file not shown.

After

Width:  |  Height:  |  Size: 986 KiB

@@ -3,16 +3,16 @@
<div class="file-preview-container" v-if="fileList && fileList.length > 0">
<div class="file-item" v-for="item in fileList" :key="item.id">
<img
v-if="item.suffix === 'jpg' || item.suffix === 'png' || item.suffix === 'jpeg' || item.suffix === 'gif'"
v-if="isImageFile(item)"
:src="item.thumbnail"
@click.stop="$commonUtil.previewFile(item)"
@click.stop="onPreviewFile(item)"
class="file-image"
/>
<img
v-else
:src="$commonUtil.getFileItemShowIcon(item.suffix)"
@click.stop="$commonUtil.previewFile(item)"
@click.stop="onPreviewFile(item)"
class="file-image"
/>
@@ -57,7 +57,12 @@ module.exports = {
},
data() {
return {
fileList: []
fileList: [],
imagePreviewInstance: null,
imagePreviewContainer: null,
imagePreviewHistoryUnsubscribe: null,
closingImagePreviewFromHistory: false,
imagePreviewLayerName: "h5-file-image-preview"
}
},
methods: {
@@ -103,9 +108,94 @@ module.exports = {
requestFullFile(ids) {
this.$axios.post("/platform/sys/file/previewFileData", {ids: JSON.stringify(ids)}).then((res) => {
if (res.code === 0) {
this.fileList = res.data
this.$set(this, "fileList", res.data || [])
}
})
},
// 判断附件是否为图片;item 传完整文件对象,返回 boolean 类型。
isImageFile(item) {
const suffix = item && item.suffix ? String(item.suffix).toLowerCase() : ""
return ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(suffix)
},
/**
* 预览附件。
* item 传 previewFileData 返回的文件对象,需包含 id、suffix、downloadPath;方法无返回值。
* 图片会与当前附件列表中的其他图片组成轮播,非图片继续使用公共文件预览逻辑。
*/
onPreviewFile(item) {
if (!this.isImageFile(item)) {
this.$commonUtil.previewFile(item)
return
}
if (this.imagePreviewInstance) return
const imageFiles = this.fileList.filter((file) => this.isImageFile(file) && file.downloadPath)
const startPosition = imageFiles.findIndex((file) => file.id === item.id)
if (startPosition < 0) return
const previewContainer = document.createElement("div")
previewContainer.style.display = "none"
imageFiles.forEach((file) => {
const image = document.createElement("img")
image.src = file.downloadPath
image.alt = file.name || ""
previewContainer.appendChild(image)
})
document.body.appendChild(previewContainer)
this.$set(this, "closingImagePreviewFromHistory", false)
this.$set(this, "imagePreviewContainer", previewContainer)
this.$set(this, "imagePreviewInstance", new Viewer(previewContainer, {
zIndex: 99999,
title: false,
initialViewIndex: startPosition,
hidden: () => { this.handleImagePreviewHidden() }
}))
this.imagePreviewInstance.show()
window.h5HistoryLayerManager.open(this.imagePreviewLayerName)
},
// Viewer 完全关闭后销毁实例和临时图片容器,并按关闭来源同步浏览器历史。
handleImagePreviewHidden() {
const closedFromHistory = this.closingImagePreviewFromHistory
this.cleanupImagePreview()
if (closedFromHistory) {
this.$set(this, "closingImagePreviewFromHistory", false)
return
}
if (window.h5HistoryLayerManager.stack.includes(this.imagePreviewLayerName)) {
window.h5HistoryLayerManager.close(this.imagePreviewLayerName)
}
},
// 清理 Viewer 实例及其临时 DOM;方法无参数、无返回值。
cleanupImagePreview() {
const previewInstance = this.imagePreviewInstance
const previewContainer = this.imagePreviewContainer
this.$set(this, "imagePreviewInstance", null)
this.$set(this, "imagePreviewContainer", null)
if (previewInstance) previewInstance.destroy()
if (previewContainer && previewContainer.parentNode) {
previewContainer.parentNode.removeChild(previewContainer)
}
},
// 浏览器返回导致图片预览层出栈时,仅关闭预览实例,不再重复回退历史记录。
syncImagePreviewHistory(stack) {
if (!this.imagePreviewInstance || stack.includes(this.imagePreviewLayerName)) return
this.$set(this, "closingImagePreviewFromHistory", true)
this.imagePreviewInstance.hide()
}
},
created() {
this.$set(this, "imagePreviewHistoryUnsubscribe", window.h5HistoryLayerManager.subscribe((stack) => {
this.syncImagePreviewHistory(stack)
}))
},
beforeDestroy() {
if (this.imagePreviewHistoryUnsubscribe) this.imagePreviewHistoryUnsubscribe()
if (this.imagePreviewInstance || this.imagePreviewContainer) {
this.$set(this, "closingImagePreviewFromHistory", true)
this.cleanupImagePreview()
}
}
}
@@ -118,283 +118,7 @@ layout("/layouts/platform.html"){
</div>
</template>
<template #edit>
<div v-if="formVisible">
<el-form :model="formData" ref="formRef" label-width="130px" :rules="formRules">
<el-form-item label="活动主题" prop="name">
<el-input placeholder="请输入活动主题" v-model="formData.name" clearable></el-input>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="活动时间" prop="activityTime">
<el-date-picker
:default-time="['00:00:00']"
end-placeholder="结束时间"
start-placeholder="开始时间"
style="width: 100%"
type="datetimerange"
v-model="formData.activityTime"
value-format="yyyy-MM-dd HH:mm:ss"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="点赞时间" prop="nbTime">
<el-date-picker
:default-time="['00:00:00']"
end-placeholder="结束时间"
start-placeholder="开始时间"
style="width: 100%"
type="datetimerange"
v-model="formData.nbTime"
value-format="yyyy-MM-dd HH:mm:ss"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="每人每日点赞次数" prop="nbCount">
<el-input-number
:min="0"
placeholder="请输入每人每日点赞次数"
style="width: 100%"
v-model="formData.nbCount"
></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="主题类型" prop="subjectTypes">
<el-row class="pb5" v-for="(type,index) in formData.subjectTypes" :key="type.id">
<el-col :span="20">
<el-input placeholder="请输入名称" v-model="type.typeName"></el-input>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button @click="addType(index)" icon="el-icon-plus" type="primary"></el-button>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button
:disabled="index == 0"
@click="formData.subjectTypes.splice(index,1)"
icon="el-icon-delete"
type="danger"
></el-button>
</el-col>
<el-col class="pt5 pb5">
<el-row class="pb5" v-for="(worksType,worksIndex) in type.worksTypes" :key="worksType.id">
<el-col :span="18">
<el-col :span="2">
<span class="el-form-item__label" v-if="worksIndex === 0">作品类型</span>
<span v-else>&nbsp;</span>
</el-col>
<el-col :span="22">
<el-input placeholder="请输入名称" v-model="worksType.worksTypeName"></el-input>
</el-col>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button @click="openSetting(worksType)" icon="el-icon-s-tools"
type="primary"></el-button>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button @click="addWorksType(type, worksIndex)" icon="el-icon-plus"
type="primary"></el-button>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button @click="type.worksTypes.splice(worksIndex, 1)" icon="el-icon-delete"
type="danger"></el-button>
</el-col>
</el-row>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="面向对象" prop="joinCnd">
<div style="display: flex; justify-content: space-between">
<div style="width: 99%">
<el-select clearable filterable placeholder="请选择参加人员范围" style="width: 99%"
v-model="formData.activityGroupId">
<el-option
:label="item.groupName"
:value="item.groupId"
v-for="item in activityGroupList"
:key="item.groupId"
></el-option>
</el-select>
</div>
<div>
<el-button @click="$refs.drawerUserScope.userScopeDialog = true" type="primary">设置
</el-button>
</div>
</div>
</el-form-item>
<el-row :gutter="10">
<el-col :span="12">
<el-form-item prop="hostUnitIds" label="主办单位">
<el-select v-model="formData.hostUnitIds" clearable filterable multiple
placeholder="请选择主办单位"
style="width: 100%">
<el-option
v-for="item in unitOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="undertakeUnitIds" label="承办单位">
<el-select v-model="formData.undertakeUnitIds" clearable filterable multiple
placeholder="请选择承办单位"
style="width: 100%">
<el-option
v-for="item in unitOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="活动内容" prop="content">
<text-editor v-model="formData.content"></text-editor>
</el-form-item>
<!--<el-form-item label="活动介绍" prop="introduce">
<text-editor v-model="formData.introduce"></text-editor>
</el-form-item>
<el-form-item label="奖励方式" prop="rewardMethod">
<text-editor v-model="formData.rewardMethod"></text-editor>
</el-form-item>-->
<el-form-item label="活动封面">
<file-upload
:upload_number="1"
:value.sync="formData.cover"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval"
></file-upload>
</el-form-item>
<el-form-item label="配色" prop="activityColor">
<el-color-picker v-model="formData.activityColor" show-alpha></el-color-picker>
</el-form-item>
<el-form-item prop="enable" label="状态">
<el-radio-group v-model="formData.enable" size="small">
<el-radio :label="true" border>开启</el-radio>
<el-radio :label="false" border>关闭</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-row class="mt20" justify="end" type="flex">
<el-button @click="close">取消</el-button>
<el-button type="primary" v-if="copyTemplateMode || formData.isSubmit!=true" plain @click="doSave">{{ copyTemplateMode ? "另存为" : "保存" }}</el-button>
<el-button type="primary" @click="doSubmit">{{ copyTemplateMode ? "新活动提交" : "确定" }}</el-button>
</el-row>
</div>
</template>
</guava>
<drawer-user-scope v-if="formVisible" :group_id.sync="formData.activityGroupId" @group_change="getActivityGroup"
ref="drawerUserScope"></drawer-user-scope>
<el-dialog v-if="formVisible" :close-on-click-modal="false" :visible.sync="settingDialogVisible" title="更多设置" top="4%" width="40%">
<el-form :model="currentWorksType" label-width="160px" size="small">
<el-form-item label="作品介绍最多字数">
<el-input-number :max="10000" :min="1" style="width: 100%"
v-model="currentWorksType.maxWordCount"></el-input-number>
</el-form-item>
<el-form-item label="允许上传的文件类型" prop="allowFileTypes">
<el-checkbox-group v-model="currentWorksType.allowFileTypes">
<el-checkbox key="jpg" label="jpg">jpg</el-checkbox>
<el-checkbox key="jpeg" label="jpeg">jpeg</el-checkbox>
<el-checkbox key="png" label="png">png</el-checkbox>
<el-checkbox key="mp3" label="mp3">mp3</el-checkbox>
<el-checkbox key="mp4" label="mp4">mp4</el-checkbox>
<el-checkbox key="docx" label="docx">docx</el-checkbox>
<el-checkbox key="xlsx" label="xlsx">xlsx</el-checkbox>
<el-checkbox key="pdf" label="pdf">pdf</el-checkbox>
<el-checkbox key="zip" label="zip">zip</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="允许上传的文件数量" prop="allowFileNum">
<el-input-number :max="10000" :min="1" style="width: 100%"
v-model="currentWorksType.allowFileNum"></el-input-number>
</el-form-item>
<el-form-item label="单个文件大小(kb)" prop="allowSingleFileSize">
<el-input-number :min="1" style="width: 100%"
v-model="currentWorksType.allowSingleFileSize"></el-input-number>
</el-form-item>
<el-form-item label="总文件大小(kb)" prop="allowTotalFileSize">
<el-input-number :min="1" style="width: 100%"
v-model="currentWorksType.allowTotalFileSize"></el-input-number>
</el-form-item>
<!-- <div class="left-span-label">上传图片设置</div>-->
<!-- <el-form-item label="是否上传图片">-->
<!-- <el-radio :label="true" border v-model="currentWorksType.isUploadPhoto">上传</el-radio>-->
<!-- <el-radio :label="false" border v-model="currentWorksType.isUploadPhoto">不上传</el-radio>-->
<!-- </el-form-item>-->
<!-- <template v-if="currentWorksType.isUploadPhoto === true">-->
<!-- <el-form-item label="上传图片数量">-->
<!-- <el-input-number :max="5" :min="1" style="width: 100%" v-model="currentWorksType.photoNum"></el-input-number>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="上传图片格式">-->
<!-- <el-checkbox-group v-model="currentWorksType.photoType">-->
<!-- <el-checkbox border key="jpg" label="jpg">jpg</el-checkbox>-->
<!-- <el-checkbox border key="png" label="png">png</el-checkbox>-->
<!-- <el-checkbox border key="jpeg" label="jpeg">jpeg</el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="每张图片大小">-->
<!-- <el-input placeholder="请填写每张图片大小" type="number" v-model="currentWorksType.photoSize">-->
<!-- <template slot="append">Mb</template>-->
<!-- </el-input>-->
<!-- </el-form-item>-->
<!-- </template>-->
<!-- <div class="left-span-label">上传音/视频、文档设置</div>-->
<!-- <el-form-item label="是否上传音/视频、文档">-->
<!-- <el-radio :label="true" border v-model="currentWorksType.isUploadOther">上传</el-radio>-->
<!-- <el-radio :label="false" border v-model="currentWorksType.isUploadOther">不上传</el-radio>-->
<!-- </el-form-item>-->
<!-- <template v-if="currentWorksType.isUploadOther === true">-->
<!-- <el-form-item label="文件上传数量">-->
<!-- <el-input-number :max="5" :min="1" style="width: 100%" v-model="currentWorksType.otherFileNum"></el-input-number>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="上传文件格式">-->
<!-- <el-checkbox-group v-model="currentWorksType.otherType">-->
<!-- <el-checkbox key="mp3" label="mp3">mp3</el-checkbox>-->
<!-- <el-checkbox key="mp4" label="mp4">mp4</el-checkbox>-->
<!-- <el-checkbox key="docx" label="docx">docx</el-checkbox>-->
<!-- <el-checkbox key="xlsx" label="xlsx">xlsx</el-checkbox>-->
<!-- <el-checkbox key="pdf" label="pdf">pdf</el-checkbox>-->
<!-- </el-checkbox-group>-->
<!-- </el-form-item>-->
<!-- <el-form-item label="每个文件大小">-->
<!-- <el-input placeholder="请填写每个文件大小" type="number" v-model="currentWorksType.otherSize">-->
<!-- <template slot="append">Mb</template>-->
<!-- </el-input>-->
<!-- </el-form-item>-->
</template>
</el-form>
<el-row class="mt20" justify="end" type="flex">
<el-button @click="settingDialogVisible=false">取消</el-button>
<el-button @click="settingDialogVisible = false" type="primary">确定</el-button>
</el-row>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
@@ -406,31 +130,9 @@ layout("/layouts/platform.html"){
activeTab: "activity",
tableHeight: 300,
tableResizeObserver: null,
activityGroupList: [],
dialogFormVisible: false,
formRules: {
name: [{required: true, message: "必填", trigger: ["change", "blur"]}],
activityTime: [{required: true, message: "必填", trigger: ["change", "blur"]}],
content: [{required: true, message: "必填", trigger: ["change", "blur"]}],
subjectTypes: [{required: true, message: "必填", trigger: ["change", "blur"]}],
enable: [{required: true, message: "必填", trigger: ["change", "blur"]}]
},
currentWorksType: {
allowFileTypes: [],
},
settingDialogVisible: false,
colorList: ["#081776", "#31cab1", "#e54242", "#ff8939", "#ffb712", "#1dc47b", "#4495f7", "#ff5e8b", "#5d6e7f"],
pageForm: {
activityTime: []
},
formVisible: false,
copyTemplateMode: false,
copyTemplateName: "",
formOptionsLoaded: false,
formOptionsPromise: null,
unitOptions: [],
clubOptions: [],
}
},
computed: {
@@ -439,53 +141,13 @@ layout("/layouts/platform.html"){
return this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
}
},
components: {
"drawer-user-scope": httpVueLoader("/components/module/activity/DrawerUserScope.vue")
},
watch: {
"formData.activityGroupId": {
async handler(newVal, oldVal) {
this.activityGroupList = await this.getActivityGroup()
this.userScopeDialog = false
},
deep: true
}
},
methods: {
isNewPage() {
return window.location.pathname.toLowerCase() === "/platform/activity/workscollection/new"
},
backToManage() {
commonUtil.pjaxPush("/platform/activity/worksCollection/manage")
},
afterSaveSuccess() {
this.formVisible = false
if (this.isNewPage()) {
this.backToManage()
} else {
this.doSearch()
this.$refs.guava.index()
}
},
initNewPage() {
const params = new URLSearchParams(window.location.search)
const mode = params.get("mode")
const id = params.get("id")
if (mode === "edit" && id) {
this.openEdit(id, true)
} else {
this.openAdd()
}
},
getActivityGroup() {
return this.$axios.post("/platform/activity/basic/scope/getActivityUserScopeGroup").then((res) => res.data)
},
dropdownCommand(command) {
const {type, row} = command
if (type === "sendNotice") {
this.sendNotice(row.id)
} else if (type === "edit") {
this.openEdit(row.id)
commonUtil.pjaxPush("/platform/activity/worksCollection/new?mode=update&id=" + row.id)
} else if (type === "setTemplate") {
this.setTemplate(row)
} else if (type === "cancelTemplate") {
@@ -537,212 +199,8 @@ layout("/layouts/platform.html"){
})
})
},
async loadFormOptions() {
if (this.formOptionsLoaded) {
return
}
if (this.formOptionsPromise) {
return this.formOptionsPromise
}
this.formOptionsPromise = Promise.all([
this.getActivityGroup(),
this.getClubsByRole(),
this.$businessTool.listUnit()
]).then(([activityGroupList, clubOptions, units]) => {
this.activityGroupList = activityGroupList
this.clubOptions = clubOptions
this.clubOptions.map((v) => {
v.name = v.clubName
})
this.unitOptions = this.clubOptions.concat(units)
this.formOptionsLoaded = true
}).finally(() => {
this.formOptionsPromise = null
})
return this.formOptionsPromise
},
addType(index) {
let item = {}
this.initData(item)
this.formData.subjectTypes.splice(index + 1, 0, {worksTypes: [item]})
},
openSetting(worksType) {
this.currentWorksType = worksType
this.settingDialogVisible = true
},
addWorksType(type, worksIndex) {
let item = {}
this.initData(item)
type.worksTypes.splice(worksIndex + 1, 0, item)
},
initData(worksType) {
this.$set(worksType, "maxWordCount", worksType.maxWordCount ? worksType.maxWordCount : 100)
this.$set(worksType, "isUploadPhoto", worksType.isUploadPhoto ? worksType.isUploadPhoto : false)
this.$set(worksType, "photoType", worksType.photoType ? worksType.photoType : ["jpg"])
this.$set(worksType, "photoNum", worksType.photoNum ? worksType.photoNum : 1)
this.$set(worksType, "photoSize", worksType.photoSize ? worksType.photoSize : 2)
this.$set(worksType, "isUploadOther", worksType.isUploadOther ? worksType.isUploadOther : false)
this.$set(worksType, "otherType", worksType.otherType ? worksType.otherType : ["mp4", "docx"])
this.$set(worksType, "otherFileNum", worksType.otherFileNum ? worksType.otherFileNum : 1)
this.$set(worksType, "otherSize", worksType.otherSize ? worksType.otherSize : 2)
this.$set(worksType, "allowFileTypes", worksType.allowFileTypes ? worksType.allowFileTypes : [])
},
async openAdd() {
this.copyTemplateMode = false
this.copyTemplateName = ""
await this.loadFormOptions()
this.formVisible = true
this.$refs.guava.edit()
this.$nextTick(() => {
this.formData = {
nbCount: 0,
activityColor: this.colorList[0],
subjectTypes: [{worksTypes: [{}]}],
enable: true
}
this.initData(this.formData.subjectTypes[0].worksTypes[0])
})
},
cleanCopyTemplateData(data) {
if (!data) {
return
}
data.id = null
data.isTemplate = false
data.templateStatus = 0
data.createdBy = null
data.createdAt = null
data.updatedBy = null
data.updatedAt = null
data.delFlag = null
if (Array.isArray(data.subjectTypes)) {
data.subjectTypes = data.subjectTypes.map((subjectType) => {
subjectType.id = null
subjectType.activityId = null
subjectType.createdBy = null
subjectType.createdAt = null
subjectType.updatedBy = null
subjectType.updatedAt = null
subjectType.delFlag = null
if (Array.isArray(subjectType.worksTypes)) {
subjectType.worksTypes = subjectType.worksTypes.map((worksType) => {
worksType.id = null
worksType.subjectId = null
worksType.createdBy = null
worksType.createdAt = null
worksType.updatedBy = null
worksType.updatedAt = null
worksType.delFlag = null
return worksType
})
}
return subjectType
})
}
},
validateCopyTemplateName() {
if (!this.copyTemplateMode) {
return true
}
const currentName = (this.formData.name || "").trim()
const templateName = (this.copyTemplateName || "").trim()
if (currentName && templateName && currentName === templateName) {
this.$message.warning("请修改活动名称,不能与模板名称一致")
return false
}
return true
},
buildSubmitData() {
const data = clone(this.formData)
data.startDateTime = data.activityTime[0]
data.endDateTime = data.activityTime[1]
data.nbStartDateTime = data.nbTime ? data.nbTime[0] : null
data.nbEndDateTime = data.nbTime ? data.nbTime[1] : null
data.type = "1"
if (this.copyTemplateMode) {
this.cleanCopyTemplateData(data)
}
return data
},
async openEdit(id, copyTemplateMode = false) {
this.copyTemplateMode = !!copyTemplateMode
const [resp] = await Promise.all([
this.$axios.post("/platform/activity/worksCollection/manage/findOne", {id}),
this.loadFormOptions()
])
this.formVisible = true
this.formData = resp.data
if (this.copyTemplateMode) {
this.copyTemplateName = this.formData.name
this.cleanCopyTemplateData(this.formData)
} else {
this.copyTemplateName = ""
}
this.$set(this.formData, "activityTime", [this.formData.startDateTime, this.formData.endDateTime])
// this.formData.activityTime = [this.formData.startDateTime, this.formData.endDateTime]
if (this.formData.nbStartDateTime && this.formData.nbEndDateTime) {
// this.formData.nbTime = [this.formData.nbStartDateTime, this.formData.nbEndDateTime]
this.$set(this.formData, "nbTime", [this.formData.nbStartDateTime, this.formData.nbEndDateTime])
}
this.$nextTick(() => {
this.$refs.guava.edit()
})
},
close() {
this.formVisible = false
if (this.isNewPage()) {
this.backToManage()
} else {
this.$refs.guava.index()
}
},
doSubmit() {
this.$refs.formRef.validate((valid) => {
if (valid) {
if (!this.validateCopyTemplateName()) {
return
}
const submitData = this.buildSubmitData()
let loading = this.$loading({
lock: true,
text: "数据正在提交中,请稍后...",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
})
this.$axios.post("/platform/activity/worksCollection/manage" + (this.copyTemplateMode || !this.formData.id ? "/insert" : "/update"), {data: JSON.stringify(submitData)}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.afterSaveSuccess()
} else {
this.$message.warning(res.msg)
}
loading.close()
})
}
})
},
doSave() {
if (!this.validateCopyTemplateName()) {
return
}
const submitData = this.buildSubmitData()
let loading = this.$loading({
lock: true,
text: "数据正在保存中,请稍后...",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
})
this.$axios.post("/platform/activity/worksCollection/manage" + (this.copyTemplateMode || !this.formData.id ? "/save" : "/update"), {data: JSON.stringify(submitData)}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.afterSaveSuccess()
} else {
this.$message.warning(res.msg)
}
loading.close()
})
openAdd() {
commonUtil.pjaxPush("/platform/activity/worksCollection/new")
},
doDelete(id) {
const row = this.tableData.find((item) => item.id === id)
@@ -819,29 +277,15 @@ layout("/layouts/platform.html"){
})
})
},
getClubsByRole() {
return this.$axios.post("/platform/club/examine/apply/getClubsByRole").then((resp) => resp.data)
},
},
async created() {
if (!this.isNewPage() && this.canManageTemplate) {
created() {
if (this.canManageTemplate) {
// 有模板管理角色时默认进入活动列表,只展示未设置模板的活动。
this.$set(this.pageForm, "templateStatus", 0)
}
if (this.isNewPage()) {
await this.loadFormOptions()
this.$nextTick(() => {
this.initNewPage()
})
} else {
this.pageData()
this.loadFormOptions()
}
this.pageData()
},
mounted() {
if (this.isNewPage()) {
return
}
const listCardBody = this.$el.querySelector(".works-collection-list-card > .el-card__body")
if (listCardBody && window.ResizeObserver) {
// 查询区换行、页签权限或窗口变化时同步调整表格内部滚动区域。
@@ -0,0 +1,467 @@
const worksCollectionBasicForm = {
template: /*language=HTML*/ `
<div>
<custom-card shadow="never">
<div slot="header">{{pageTitle}}</div>
<el-form :model="formData" ref="formRef" label-width="130px" :rules="formRules">
<el-form-item label="活动主题" prop="name">
<el-input placeholder="请输入活动主题" v-model="formData.name" clearable></el-input>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="活动时间" prop="activityTime">
<el-date-picker
:default-time="['00:00:00']"
end-placeholder="结束时间"
start-placeholder="开始时间"
style="width: 100%"
type="datetimerange"
v-model="formData.activityTime"
value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="点赞时间" prop="nbTime">
<el-date-picker
:default-time="['00:00:00']"
end-placeholder="结束时间"
start-placeholder="开始时间"
style="width: 100%"
type="datetimerange"
v-model="formData.nbTime"
value-format="yyyy-MM-dd HH:mm:ss">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="每人每日点赞次数" prop="nbCount">
<el-input-number
:min="0"
placeholder="请输入每人每日点赞次数"
style="width: 100%"
v-model="formData.nbCount">
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="主题类型" prop="subjectTypes">
<el-row class="pb5" v-for="(type,index) in formData.subjectTypes" :key="type.id || index">
<el-col :span="20">
<el-input placeholder="请输入名称" v-model="type.typeName"></el-input>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button @click="addType(index)" icon="el-icon-plus" type="primary"></el-button>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button
:disabled="index === 0"
@click="removeType(index)"
icon="el-icon-delete"
type="danger">
</el-button>
</el-col>
<el-col class="pt5 pb5">
<el-row class="pb5" v-for="(worksType,worksIndex) in type.worksTypes"
:key="worksType.id || worksIndex">
<el-col :span="18">
<el-col :span="2">
<span class="el-form-item__label" v-if="worksIndex === 0">作品类型</span>
<span v-else>&nbsp;</span>
</el-col>
<el-col :span="22">
<el-input placeholder="请输入名称" v-model="worksType.worksTypeName"></el-input>
</el-col>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button @click="openSetting(worksType)" icon="el-icon-s-tools"
type="primary"></el-button>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button @click="addWorksType(type, worksIndex)" icon="el-icon-plus"
type="primary"></el-button>
</el-col>
<el-col :span="2" style="text-align: center">
<el-button :disabled="type.worksTypes.length === 1"
@click="removeWorksType(type, worksIndex)" icon="el-icon-delete"
type="danger"></el-button>
</el-col>
</el-row>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="面向对象" prop="joinCnd">
<div style="display: flex; justify-content: space-between">
<div style="width: 99%">
<el-select clearable filterable placeholder="请选择参加人员范围" style="width: 99%"
v-model="formData.activityGroupId">
<el-option
:label="item.groupName"
:value="item.groupId"
v-for="item in activityGroupList"
:key="item.groupId">
</el-option>
</el-select>
</div>
<div>
<el-button @click="$refs.drawerUserScope.userScopeDialog = true" type="primary">设置</el-button>
</div>
</div>
</el-form-item>
<el-row :gutter="10">
<el-col :span="12">
<el-form-item prop="hostUnitIds" label="主办单位">
<el-select v-model="formData.hostUnitIds" clearable filterable multiple
placeholder="请选择主办单位" style="width: 100%">
<el-option v-for="item in unitOptions" :key="item.id"
:label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="undertakeUnitIds" label="承办单位">
<el-select v-model="formData.undertakeUnitIds" clearable filterable multiple
placeholder="请选择承办单位" style="width: 100%">
<el-option v-for="item in unitOptions" :key="item.id"
:label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="活动内容" prop="content">
<text-editor v-model="formData.content"></text-editor>
</el-form-item>
<el-form-item label="活动封面">
<file-upload
:upload_number="1"
:value.sync="formData.cover"
accept=".jpg,.jpeg,.png"
complete_result
upload_mode="image"
upload_result_category="interval">
</file-upload>
</el-form-item>
<el-form-item label="配色" prop="activityColor">
<el-color-picker v-model="formData.activityColor" show-alpha></el-color-picker>
</el-form-item>
<el-form-item prop="enable" label="状态">
<el-radio-group v-model="formData.enable" size="small">
<el-radio :label="true" border>开启</el-radio>
<el-radio :label="false" border>关闭</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template slot="footer">
<el-button :disabled="formLoading" @click="backToManage">取消</el-button>
<el-button :loading="formLoading" type="primary"
v-if="copyTemplateMode || formData.isSubmit !== true"
plain @click="doSave">{{copyTemplateMode ? "另存为" : "保存"}}</el-button>
<el-button :loading="formLoading" type="primary" @click="doSubmit">
{{copyTemplateMode ? "新活动提交" : "确定"}}
</el-button>
</template>
</custom-card>
<drawer-user-scope :group_id.sync="formData.activityGroupId" @group_change="refreshActivityGroup"
ref="drawerUserScope"></drawer-user-scope>
<el-dialog :close-on-click-modal="false" :visible.sync="settingDialogVisible"
title="更多设置" top="4%" width="40%">
<el-form :model="currentWorksType" label-width="160px" size="small">
<el-form-item label="作品介绍最多字数">
<el-input-number :max="10000" :min="1" style="width: 100%"
v-model="currentWorksType.maxWordCount"></el-input-number>
</el-form-item>
<el-form-item label="允许上传的文件类型" prop="allowFileTypes">
<el-checkbox-group v-model="currentWorksType.allowFileTypes">
<el-checkbox key="jpg" label="jpg">jpg</el-checkbox>
<el-checkbox key="jpeg" label="jpeg">jpeg</el-checkbox>
<el-checkbox key="png" label="png">png</el-checkbox>
<el-checkbox key="mp3" label="mp3">mp3</el-checkbox>
<el-checkbox key="mp4" label="mp4">mp4</el-checkbox>
<el-checkbox key="docx" label="docx">docx</el-checkbox>
<el-checkbox key="xlsx" label="xlsx">xlsx</el-checkbox>
<el-checkbox key="pdf" label="pdf">pdf</el-checkbox>
<el-checkbox key="zip" label="zip">zip</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="允许上传的文件数量" prop="allowFileNum">
<el-input-number :max="10000" :min="1" style="width: 100%"
v-model="currentWorksType.allowFileNum"></el-input-number>
</el-form-item>
<el-form-item label="单个文件大小(kb)" prop="allowSingleFileSize">
<el-input-number :min="1" style="width: 100%"
v-model="currentWorksType.allowSingleFileSize"></el-input-number>
</el-form-item>
<el-form-item label="总文件大小(kb)" prop="allowTotalFileSize">
<el-input-number :min="1" style="width: 100%"
v-model="currentWorksType.allowTotalFileSize"></el-input-number>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="settingDialogVisible = false">取消</el-button>
<el-button @click="settingDialogVisible = false" type="primary">确定</el-button>
</span>
</el-dialog>
</div>
`,
components: {
"drawer-user-scope": httpVueLoader("/components/module/activity/DrawerUserScope.vue")
},
data() {
return {
formData: {
nbCount: 0,
activityColor: "#081776",
subjectTypes: [],
hostUnitIds: [],
undertakeUnitIds: [],
enable: true
},
formLoading: false,
copyTemplateMode: false,
copyTemplateName: "",
activityGroupList: [],
unitOptions: [],
currentWorksType: {allowFileTypes: []},
settingDialogVisible: false,
colorList: ["#081776", "#31cab1", "#e54242", "#ff8939", "#ffb712", "#1dc47b", "#4495f7", "#ff5e8b", "#5d6e7f"],
formRules: {
name: [{required: true, message: "必填", trigger: ["change", "blur"]}],
activityTime: [{required: true, message: "必填", trigger: ["change", "blur"]}],
content: [{required: true, message: "必填", trigger: ["change", "blur"]}],
subjectTypes: [{required: true, message: "必填", trigger: ["change", "blur"]}],
enable: [{required: true, message: "必填", trigger: ["change", "blur"]}]
}
}
},
computed: {
pageTitle() {
if (this.copyTemplateMode) {
return "从模板创建作品征集活动"
}
return this.formData.id ? "编辑作品征集活动" : "新建作品征集活动"
}
},
methods: {
/**
* 初始化独立活动表单。
* URL 参数:无 id 时创建空白活动;mode=update&id=活动ID 时编辑原活动;
* mode=edit&id=模板活动ID 时复制模板。完成后无返回值,通过组件事件通知页面返回。
*/
initData() {
Promise.all([this.loadFormOptions(), this.initForm()]).catch(() => {
this.$message.error("活动表单初始化失败,请稍后重试")
})
},
// 创建新增页面的默认表单数据,并初始化第一组主题和作品类型。
createEmptyFormData() {
const worksType = {}
this.initWorksType(worksType)
return {
nbCount: 0,
activityColor: "#081776",
subjectTypes: [{worksTypes: [worksType]}],
hostUnitIds: [],
undertakeUnitIds: [],
enable: true
}
},
initWorksType(worksType) {
this.$set(worksType, "maxWordCount", worksType.maxWordCount || 100)
this.$set(worksType, "isUploadPhoto", worksType.isUploadPhoto || false)
this.$set(worksType, "photoType", worksType.photoType || ["jpg"])
this.$set(worksType, "photoNum", worksType.photoNum || 1)
this.$set(worksType, "photoSize", worksType.photoSize || 2)
this.$set(worksType, "isUploadOther", worksType.isUploadOther || false)
this.$set(worksType, "otherType", worksType.otherType || ["mp4", "docx"])
this.$set(worksType, "otherFileNum", worksType.otherFileNum || 1)
this.$set(worksType, "otherSize", worksType.otherSize || 2)
this.$set(worksType, "allowFileTypes", worksType.allowFileTypes || [])
},
addType(index) {
const worksType = {}
this.initWorksType(worksType)
this.formData.subjectTypes.splice(index + 1, 0, {worksTypes: [worksType]})
},
removeType(index) {
this.formData.subjectTypes.splice(index, 1)
},
addWorksType(type, worksIndex) {
const worksType = {}
this.initWorksType(worksType)
type.worksTypes.splice(worksIndex + 1, 0, worksType)
},
removeWorksType(type, worksIndex) {
type.worksTypes.splice(worksIndex, 1)
},
openSetting(worksType) {
this.initWorksType(worksType)
this.currentWorksType = worksType
this.settingDialogVisible = true
},
getActivityGroup() {
return this.$axios.post("/platform/activity/basic/scope/getActivityUserScopeGroup")
.then((res) => res.data || [])
},
getClubsByRole() {
return this.$axios.post("/platform/club/examine/apply/getClubsByRole")
.then((res) => res.data || [])
},
loadFormOptions() {
return Promise.all([
this.getActivityGroup(),
this.getClubsByRole(),
this.$businessTool.listUnit()
]).then(([activityGroupList, clubOptions, units]) => {
this.activityGroupList = activityGroupList
clubOptions.forEach((item) => {
this.$set(item, "name", item.clubName)
})
this.unitOptions = clubOptions.concat(units || [])
})
},
refreshActivityGroup() {
this.getActivityGroup().then((activityGroupList) => {
this.activityGroupList = activityGroupList
})
},
// 模板仅作为新活动的数据来源,清理主表和子表标识以防止更新模板原记录。
cleanCopyTemplateData(data) {
data.id = null
data.isTemplate = false
data.templateStatus = 0
data.createdBy = null
data.createdAt = null
data.updatedBy = null
data.updatedAt = null
data.delFlag = null
;(data.subjectTypes || []).forEach((subjectType) => {
subjectType.id = null
subjectType.activityId = null
subjectType.createdBy = null
subjectType.createdAt = null
subjectType.updatedBy = null
subjectType.updatedAt = null
subjectType.delFlag = null
;(subjectType.worksTypes || []).forEach((worksType) => {
worksType.id = null
worksType.subjectId = null
worksType.createdBy = null
worksType.createdAt = null
worksType.updatedBy = null
worksType.updatedAt = null
worksType.delFlag = null
})
})
},
initForm() {
const mode = GetQueryString("mode")
const id = GetQueryString("id")
this.copyTemplateMode = mode === "edit" && !!id
if (!id) {
this.formData = this.createEmptyFormData()
return Promise.resolve()
}
return this.$axios.post("/platform/activity/worksCollection/manage/findOne", {id})
.then((res) => {
if (res.code !== 0 || !res.data) {
return
}
const data = res.data
if (this.copyTemplateMode) {
this.copyTemplateName = data.name
this.cleanCopyTemplateData(data)
}
this.$set(data, "activityTime", [data.startDateTime, data.endDateTime])
this.$set(data, "nbTime", data.nbStartDateTime && data.nbEndDateTime
? [data.nbStartDateTime, data.nbEndDateTime] : [])
this.$set(data, "hostUnitIds", data.hostUnitIds || [])
this.$set(data, "undertakeUnitIds", data.undertakeUnitIds || [])
if (!data.subjectTypes || !data.subjectTypes.length) {
const worksType = {}
this.initWorksType(worksType)
this.$set(data, "subjectTypes", [{worksTypes: [worksType]}])
}
;(data.subjectTypes || []).forEach((subjectType) => {
this.$set(subjectType, "worksTypes", subjectType.worksTypes || [])
if (!subjectType.worksTypes.length) {
const worksType = {}
this.initWorksType(worksType)
subjectType.worksTypes.push(worksType)
}
subjectType.worksTypes.forEach((worksType) => this.initWorksType(worksType))
})
this.formData = data
})
},
validateCopyTemplateName() {
if (!this.copyTemplateMode) {
return true
}
const currentName = (this.formData.name || "").trim()
const templateName = (this.copyTemplateName || "").trim()
if (currentName && templateName && currentName === templateName) {
this.$message.warning("请修改活动名称,不能与模板名称一致")
return false
}
return true
},
buildSubmitData() {
const data = clone(this.formData)
data.startDateTime = data.activityTime && data.activityTime.length ? data.activityTime[0] : null
data.endDateTime = data.activityTime && data.activityTime.length ? data.activityTime[1] : null
data.nbStartDateTime = data.nbTime && data.nbTime.length ? data.nbTime[0] : null
data.nbEndDateTime = data.nbTime && data.nbTime.length ? data.nbTime[1] : null
data.type = "1"
if (this.copyTemplateMode) {
this.cleanCopyTemplateData(data)
}
return data
},
submitRequest(path) {
if (!this.validateCopyTemplateName()) {
return
}
this.formLoading = true
this.$axios.post("/platform/activity/worksCollection/manage" + path, {
data: JSON.stringify(this.buildSubmitData())
}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.backToManage()
} else {
this.$message.warning(res.msg)
}
}).finally(() => {
this.formLoading = false
})
},
doSave() {
const path = this.copyTemplateMode || !this.formData.id ? "/save" : "/update"
this.submitRequest(path)
},
doSubmit() {
this.$refs.formRef.validate((valid) => {
if (!valid) {
return
}
const path = this.copyTemplateMode || !this.formData.id ? "/insert" : "/update"
this.submitRequest(path)
})
},
backToManage() {
this.$emit("back")
}
}
}
@@ -0,0 +1,35 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<works-collection-basic-form ref="formRef" @back="back"></works-collection-basic-form>
</div>
<script nonce="${cspNonce!}">
<!--#include("basicForm.js"){}#-->
new Vue({
el: "#app",
store,
components: {
"works-collection-basic-form": worksCollectionBasicForm
},
methods: {
back() {
commonUtil.pjaxPush("/platform/activity/worksCollection/manage")
},
initForm() {
this.$refs.formRef.initData()
}
},
mounted() {
this.$nextTick(() => {
this.initForm()
})
}
})
</script>
<!--#
}
#-->
@@ -1,47 +1,474 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app">
<!-- 导航栏 -->
<style>
body {
background-color: #f3f7fd;
}
<van-nav-bar title="作品上传" left-text="返回" left-arrow placeholder
@click-left="historyBack" fixed></van-nav-bar>
<van-sticky offset-top="46px">
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<year-van-dropdown-item :num="5" @change="doSearch" v-model="pageForm.year"></year-van-dropdown-item>
</van-dropdown-menu>
</van-sticky>
<table-list api="/platform/activity/worksCollection/upload/pageData"
:page_form.sync="pageForm"
ref="tableListRef"
title="name"
@ready="onReady"
>
<template v-slot="{index,row}">
<table-column label="主题类型">{{row.subjectName}}</table-column>
<table-column label="作品类型">{{row.worksName}}</table-column>
<table-column label="姓名">{{row.userName}}</table-column>
<table-column label="上传时间">{{$moment(row.createdAt).format('YYYY-MM-DD')}}</table-column>
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<span>查看</span>
.works-mine-page {
min-height: 100vh;
padding-bottom: calc(24px + env(safe-area-inset-bottom));
box-sizing: border-box;
background-color: #f3f7fd;
}
.works-mine-page .van-nav-bar {
background-color: #fff;
}
.works-mine-page .van-nav-bar__title {
color: #1f2937;
font-size: 16px;
}
.works-mine-page .van-nav-bar .van-icon,
.works-mine-page .van-nav-bar__text {
color: #397fbd;
}
.works-mine-content {
padding: 12px 12px 0;
}
.works-mine-hero {
position: relative;
height: 134px;
overflow: hidden;
border-radius: 10px;
color: #fff;
background-color: #247fe8;
box-shadow: 0 8px 20px rgba(51, 117, 219, .18);
}
.works-mine-hero__image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.works-mine-hero__copy {
position: relative;
z-index: 1;
display: flex;
width: 66%;
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%);
}
.works-mine-hero__title {
font-size: 25px;
line-height: 34px;
font-weight: 700;
letter-spacing: 1px;
}
.works-mine-hero__subtitle {
margin-top: 4px;
font-size: 13px;
line-height: 20px;
letter-spacing: .5px;
opacity: .95;
}
.works-mine-sticky {
padding: 10px 0 1px;
background-color: #f3f7fd;
}
.van-sticky--fixed .works-mine-sticky {
padding-right: 12px;
padding-left: 12px;
box-sizing: border-box;
}
.works-mine-search {
margin-bottom: 8px;
padding: 0;
overflow: hidden;
border-radius: 12px;
background-color: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
}
.works-mine-search .van-search__content {
height: 42px;
padding-left: 12px;
align-items: center;
border-radius: 12px;
background-color: #fff;
}
.works-mine-filter {
margin-bottom: 10px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
transition: border-radius .18s ease;
}
.works-mine-filter.is-open {
border-radius: 10px 10px 0 0;
}
.works-mine-filter .van-dropdown-menu__bar {
height: 44px;
box-shadow: none;
}
.works-mine-filter .van-dropdown-menu__item {
min-width: 0;
overflow: hidden;
}
.works-mine-filter .van-dropdown-menu__title {
width: auto;
max-width: calc(100% - 24px);
padding: 0 12px 0 0;
overflow: hidden;
color: #66758a;
font-size: 14px;
text-overflow: ellipsis;
white-space: nowrap;
}
.works-mine-filter .van-dropdown-menu__title::after {
right: 0;
}
.works-mine-filter .van-dropdown-item__content {
width: calc(100% - 24px);
margin: 0 12px;
overflow: hidden;
border-radius: 0 0 12px 12px;
box-shadow: 0 10px 24px rgba(31, 65, 108, .14);
box-sizing: border-box;
}
.works-mine-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.works-mine-card {
overflow: hidden;
border-radius: 10px;
background-color: #fff;
box-shadow: 0 7px 20px rgba(55, 85, 126, .08);
}
.works-mine-card__cover,
.works-mine-card__cover-state {
display: flex;
width: 100%;
height: 150px;
align-items: center;
justify-content: center;
background-color: #edf4fc;
}
.works-mine-card__cover {
display: block;
object-fit: cover;
}
.works-mine-card__cover-state {
flex-direction: column;
row-gap: 8px;
color: #8da1ba;
font-size: 13px;
}
.works-mine-card__cover-state .van-icon {
font-size: 34px;
}
.works-mine-card__cover-state.is-error {
color: #c27b75;
background-color: #fff3f1;
}
.works-mine-card__body {
padding: 12px 14px 14px;
}
.works-mine-card__title {
overflow: hidden;
color: #303846;
font-size: 15px;
line-height: 22px;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
.works-mine-card__activity,
.works-mine-card__time {
margin-top: 4px;
overflow: hidden;
color: #8794a7;
font-size: 12px;
line-height: 18px;
text-overflow: ellipsis;
white-space: nowrap;
}
.works-mine-card__meta {
display: flex;
margin-top: 10px;
padding-top: 9px;
flex-wrap: wrap;
gap: 6px;
border-top: 1px solid #edf1f6;
}
.works-mine-card__tag {
display: inline-flex;
padding: 3px 8px;
border-radius: 4px;
color: #397de0;
background-color: #edf5ff;
font-size: 11px;
line-height: 16px;
}
.works-mine-card__tag-label {
margin-right: 4px;
color: #8aa7cf;
}
.works-mine-card__actions {
display: flex;
padding: 12px 14px;
align-items: center;
justify-content: flex-end;
gap: 8px;
border-top: 1px solid #edf1f6;
background-color: #f8fbff;
}
.works-mine-card__action {
display: inline-flex;
min-width: 72px;
height: 32px;
padding: 0 12px;
align-items: center;
justify-content: center;
border: 0;
border-radius: 16px;
color: #397de0;
background-color: #edf5ff;
font-size: 13px;
box-sizing: border-box;
}
.works-mine-card__action .van-icon {
margin-right: 5px;
font-size: 15px;
}
.works-mine-card__action.is-primary {
color: #fff;
background-color: #1989fa;
box-shadow: 0 5px 12px rgba(25, 137, 250, .18);
}
.works-mine-card__action.is-danger {
color: #e45c5c;
background-color: #fff0f0;
}
.works-mine-skeleton {
display: flex;
flex-direction: column;
gap: 10px;
}
.works-mine-skeleton__card {
overflow: hidden;
border-radius: 10px;
background-color: #fff;
box-shadow: 0 7px 20px rgba(55, 85, 126, .08);
}
.works-mine-skeleton__cover,
.works-mine-skeleton__body .van-skeleton,
.works-mine-skeleton__actions .van-skeleton {
padding: 0;
}
.works-mine-skeleton__cover .van-skeleton__row {
width: 100% !important;
height: 150px;
margin: 0;
border-radius: 0;
}
.works-mine-skeleton__body {
padding: 13px 14px 14px;
}
.works-mine-skeleton__body .van-skeleton__title {
width: 52%;
height: 15px;
margin-bottom: 12px;
}
.works-mine-skeleton__body .van-skeleton__row {
height: 12px;
margin-top: 9px;
}
.works-mine-skeleton__body .van-skeleton__row:last-child {
width: 58%;
}
.works-mine-skeleton__actions {
padding: 12px 14px;
border-top: 1px solid #edf1f6;
background-color: #f8fbff;
}
.works-mine-skeleton__actions .van-skeleton__row {
width: 72%;
height: 32px;
margin: 0 0 0 auto;
border-radius: 16px;
}
.works-mine-empty {
display: flex;
min-height: 260px;
padding: 26px 18px 32px;
align-items: center;
justify-content: center;
flex-direction: column;
text-align: center;
}
.works-mine-empty img {
width: 82%;
max-width: 280px;
height: auto;
object-fit: contain;
}
.works-mine-empty__title {
margin-top: 14px;
color: #50627a;
font-size: 15px;
line-height: 22px;
font-weight: 600;
}
.works-mine-empty__hint {
margin-top: 5px;
color: #9aa7b8;
font-size: 12px;
line-height: 18px;
}
</style>
<div id="app" v-cloak>
<div class="works-mine-page">
<van-nav-bar title="我的作品" left-text="返回" left-arrow fixed placeholder @click-left="handlePageBack"></van-nav-bar>
<div class="works-mine-content">
<section class="works-mine-hero">
<img class="works-mine-hero__image" src="/assets/mobile/img/activity/hero/brand-hero-v2.png" alt="我的作品插画">
<div class="works-mine-hero__copy">
<div class="works-mine-hero__title">我的作品</div>
<div class="works-mine-hero__subtitle">作品征集 · 提交记录</div>
</div>
</section>
<van-sticky :offset-top="46">
<div class="works-mine-sticky">
<van-search v-model="pageForm.searchKeyword" class="works-mine-search" placeholder="搜索作品或征集活动"
shape="round" clearable @search="doSearch" @clear="doSearch"></van-search>
<van-dropdown-menu class="works-mine-filter" :class="{'is-open': filterOpened}" active-color="#1989fa">
<year-van-dropdown-item :num="5" v-model="pageForm.year" @change="doSearch"
@open="onFilterOpen" @close="onFilterClose"></year-van-dropdown-item>
<van-dropdown-item v-model="pageForm.activityType" :options="typeOptions" :multiple="false"
@change="doSearch" @open="onFilterOpen" @close="onFilterClose"></van-dropdown-item>
</van-dropdown-menu>
</div>
</van-sticky>
<div v-if="skeletonLoading" class="works-mine-skeleton">
<div v-for="index in 3" :key="'mine-skeleton-' + index" class="works-mine-skeleton__card">
<van-skeleton class="works-mine-skeleton__cover" :row="1" animated></van-skeleton>
<div class="works-mine-skeleton__body">
<van-skeleton title :row="3" animated></van-skeleton>
</div>
<div class="works-mine-skeleton__actions">
<van-skeleton :row="1" animated></van-skeleton>
</div>
</div>
</div>
<div class="action-btn" @click="onEdit(row)">
<i class="fa fa-edit"></i>
<span>编辑</span>
</div>
<div class="action-btn delete" @click="onDelete(row)">
<i class="fa fa-trash"></i>
<span>删除</span>
</div>
</template>
</table-list>
<van-pull-refresh v-show="!skeletonLoading" v-model="refreshing" @refresh="onRefresh">
<van-list v-model="loading" :finished="finished" :immediate-check="false" @load="onLoad">
<div class="works-mine-list">
<article v-for="row in tableData" :key="row.id" class="works-mine-card">
<img v-if="hasCover(row) && !imageLoadErrors[row.id]" class="works-mine-card__cover"
:src="row.cover" :alt="row.activityName" @error="handleImageError(row)">
<div v-else class="works-mine-card__cover-state" :class="{'is-error': hasCover(row)}">
<van-icon name="photo-o"></van-icon>
<span>{{hasCover(row) ? '图片加载失败' : '暂无图片'}}</span>
</div>
<div class="works-mine-card__body">
<div class="works-mine-card__title">{{row.name}}</div>
<div class="works-mine-card__activity">所属活动:{{row.activityName || '暂无活动信息'}}</div>
<div class="works-mine-card__time" v-if="row.createdAt">
提交时间:{{$moment(row.createdAt).format('YYYY-MM-DD HH:mm')}}
</div>
<div class="works-mine-card__meta" v-if="row.subjectName || row.worksName">
<span class="works-mine-card__tag" v-if="row.subjectName">
<span class="works-mine-card__tag-label">主题</span>{{row.subjectName}}
</span>
<span class="works-mine-card__tag" v-if="row.worksName">
<span class="works-mine-card__tag-label">类型</span>{{row.worksName}}
</span>
</div>
</div>
<div class="works-mine-card__actions">
<button type="button" class="works-mine-card__action" @click="onView(row)">
<van-icon name="eye-o"></van-icon>查看
</button>
<button type="button" class="works-mine-card__action is-primary" @click="onEdit(row)">
<van-icon name="edit"></van-icon>编辑
</button>
<button type="button" class="works-mine-card__action is-danger" @click="onDelete(row)">
<van-icon name="delete-o"></van-icon>删除
</button>
</div>
</article>
</div>
</van-list>
<div v-if="!loading && tableData.length === 0" class="works-mine-empty">
<img src="/assets/mobile/img/activity/culture-empty.png" alt="作品记录空状态插画">
<div class="works-mine-empty__title">暂无作品记录</div>
<div class="works-mine-empty__hint">当前筛选条件下暂时没有已提交作品</div>
</div>
</van-pull-refresh>
</div>
</div>
<van-dialog title="删除确认" message="确认删除当前作品吗?" :value="deleteConfirmVisible"
:close-on-click-overlay="false" show-cancel-button cancel-button-text="取消" confirm-button-text="确认删除"
@cancel="closeDeleteConfirm" @confirm="confirmDelete"></van-dialog>
<apply-form ref="applyFormRef"></apply-form>
<info ref="infoRef"></info>
</div>
@@ -51,22 +478,59 @@ layout("/layouts/platform_h5.html"){
new Vue({
el: "#app",
store,
components: {
'apply-form': applyForm,
'info': INFO,
},
data() {
return {
tableData: [],
loading: false,
skeletonLoading: true,
finished: false,
refreshing: false,
imageLoadErrors: {},
filterOpened: false,
deleteConfirmVisible: false,
deleteLoading: false,
pendingDeleteRow: {},
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
searchKeyword: '',
year: new Date().getFullYear(),
activityType: 1,
},
typeOptions: [
{text: '全部', value: 1},
{text: '征集中', value: 2},
{text: '已结束', value: 3},
],
historyLayerPageKey: 'works-collection-mine-h5',
historyLayerUnsubscribe: null,
}
},
components: {
'apply-form': applyForm,
'info': INFO,
},
methods: {
hasCover(row) {
return !!(row && row.cover && String(row.cover).trim())
},
handleImageError(row) {
this.$set(this.imageLoadErrors, row.id, true)
},
onFilterOpen() {
this.$set(this, 'filterOpened', true)
},
onFilterClose() {
this.$set(this, 'filterOpened', false)
},
handlePageBack() {
if (window.h5HistoryLayerManager.stack.length) {
window.h5HistoryLayerManager.close()
return
}
this.historyBack()
},
onView(row) {
this.$refs.infoRef.onOpen(row)
},
@@ -78,30 +542,89 @@ layout("/layouts/platform_h5.html"){
this.$refs.applyFormRef.onOpenEdit(row)
},
onDelete(row) {
this.$dialog.confirm({
title: '温馨提示',
message: '您确定要删除吗?',
}).then(() => {
this.$axios.post("/platform/activity/worksCollection/upload/delete", {id:row.id}).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg)
this.doSearch()
}
})
}).catch(() => {
// on cancel
});
this.$set(this, 'pendingDeleteRow', row)
window.h5HistoryLayerManager.open('works-collection-mine-delete-confirm')
},
onReady() {
this.doSearch()
closeDeleteConfirm() {
window.h5HistoryLayerManager.close('works-collection-mine-delete-confirm')
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
confirmDelete() {
const row = this.pendingDeleteRow
window.h5HistoryLayerManager.close('works-collection-mine-delete-confirm', () => {
this.deleteRow(row)
})
},
// 删除接口接收作品 id,成功后重置分页列表;接口返回 Result JSON。
deleteRow(row) {
if (!row || !row.id || this.deleteLoading) return
this.$set(this, 'deleteLoading', true)
this.$axios.post('/platform/activity/worksCollection/upload/delete', {id: row.id}).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg || '删除成功')
this.loadWorks(true)
}
}).finally(() => {
this.$set(this, 'deleteLoading', false)
})
},
onLoad() {
if (!this.finished) this.loadWorks(false)
},
onRefresh() {
this.loadWorks(true)
},
/**
* 加载当前用户作品分页数据。
* reset 为 true 时从第一页重新查询;接口返回 Paginationlist 为作品数组、totalCount 为总数。
*/
loadWorks(reset) {
const showSkeleton = !!reset
if (reset) {
this.$set(this, 'skeletonLoading', true)
this.$set(this.pageForm, 'pageNumber', 1)
this.$set(this.pageForm, 'totalCount', 0)
this.$set(this, 'tableData', [])
this.$set(this, 'finished', false)
this.$set(this, 'imageLoadErrors', {})
}
this.$set(this, 'loading', true)
this.$axios.post('/platform/activity/worksCollection/upload/pageData', this.pageForm, {
h5SkeletonLoading: showSkeleton
}).then((res) => {
if (res.code === 0) {
const list = res.data && res.data.list ? res.data.list : []
this.$set(this, 'tableData', this.pageForm.pageNumber === 1 ? list : this.tableData.concat(list))
this.$set(this.pageForm, 'totalCount', res.data && res.data.totalCount ? res.data.totalCount : 0)
this.$set(this, 'finished', this.tableData.length >= this.pageForm.totalCount)
if (!this.finished) {
this.$set(this.pageForm, 'pageNumber', this.pageForm.pageNumber + 1)
}
}
}).finally(() => {
this.$set(this, 'loading', false)
this.$set(this, 'refreshing', false)
if (showSkeleton) this.$set(this, 'skeletonLoading', false)
})
},
doSearch() {
this.loadWorks(true)
},
syncHistoryLayers(stack) {
this.$set(this, 'deleteConfirmVisible', stack.includes('works-collection-mine-delete-confirm'))
}
},
created() {
window.h5HistoryLayerManager.register(this.historyLayerPageKey, (stack) => {
this.syncHistoryLayers(stack)
})
this.$set(this, 'historyLayerUnsubscribe', window.h5HistoryLayerManager.subscribe((stack) => {
this.syncHistoryLayers(stack)
}))
this.loadWorks(true)
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe()
window.h5HistoryLayerManager.unregister(this.historyLayerPageKey)
}
})
</script>
@@ -2,63 +2,189 @@ const INFO = {
template: /*language=HTML*/
`
<div>
<van-action-sheet v-model="visible" :style="{ 'background-color': '#F7F8FA' }" title="报名信息">
<div class="detail-container">
<van-cell-group>
<van-cell title="姓名">
{{ viewData.userName }}
</van-cell>
<van-cell title="工号">
{{ viewData.loginName }}
</van-cell>
<van-cell title="单位">
{{ viewData.unitName }}
</van-cell>
<van-cell title="分工会">
{{ viewData.unionName }}
</van-cell>
<van-cell title="作品名称">
{{ viewData.name }}
</van-cell>
<van-cell title="活动主题">
{{ viewData.activityName }}
</van-cell>
<van-cell title="主题类型">
{{ viewData.subjectName }}
</van-cell>
<van-cell title="作品类型">
{{ viewData.worksName }}
</van-cell>
<van-cell title="作品描述" class="direction-column-cell">
{{ viewData.description }}
</van-cell>
<van-cell title="作品附件" class="direction-column-cell">
<file-preview :files="viewData.files" complete_result></file-preview>
</van-cell>
</van-cell-group>
<van-popup class="works-detail-popup" :style="{height: '100%', width: '100%'}" position="right"
safe-area-inset-bottom :close-on-click-overlay="false" v-model="visible">
<div class="works-detail-page">
<van-sticky>
<van-nav-bar title="作品详情" left-text="返回" left-arrow @click-left="closeInfo"></van-nav-bar>
</van-sticky>
<div class="works-detail-scroll">
<section class="works-detail-card">
<h2 class="works-detail-card__title">基础信息</h2>
<van-cell-group :border="false">
<van-cell title="姓名" :value="viewData.userName || '-' "></van-cell>
<van-cell title="工号" :value="viewData.loginName || '-' "></van-cell>
<van-cell title="单位" :value="viewData.unitName || '-' "></van-cell>
<van-cell title="分工会" :value="viewData.unionName || '-' "></van-cell>
</van-cell-group>
</section>
<section class="works-detail-card">
<h2 class="works-detail-card__title">作品信息</h2>
<van-cell-group :border="false">
<van-cell title="所属活动" :value="viewData.activityName || '-' "></van-cell>
<van-cell title="主题类型" :value="viewData.subjectName || '-' "></van-cell>
<van-cell title="作品类型" :value="viewData.worksName || '-' "></van-cell>
<van-cell title="作品名称" :value="viewData.name || '-' "></van-cell>
</van-cell-group>
</section>
<section class="works-detail-card">
<h2 class="works-detail-card__title">作品描述</h2>
<div class="works-detail-description">{{viewData.description || '暂无作品描述'}}</div>
</section>
<section class="works-detail-card">
<h2 class="works-detail-card__title">作品附件</h2>
<div v-if="viewData.files && viewData.files.length" class="works-detail-files">
<file-preview :files="viewData.files" complete_result></file-preview>
</div>
<div v-else class="works-detail-empty">暂无作品附件</div>
</section>
</div>
</div>
</van-action-sheet>
</van-popup>
</div>
`,
style: /*language=CSS*/ `
/deep/ .works-detail-popup,
/deep/ .works-detail-page {
background-color: #f3f7fd;
}
/deep/ .works-detail-page {
min-height: 100vh;
}
/deep/ .works-detail-page .van-nav-bar__title {
color: #1f2937;
font-size: 16px;
}
/deep/ .works-detail-page .van-nav-bar .van-icon,
/deep/ .works-detail-page .van-nav-bar__text {
color: #397fbd;
}
/deep/ .works-detail-scroll {
height: calc(100vh - 46px);
padding: 12px 12px calc(24px + env(safe-area-inset-bottom));
overflow-x: hidden;
overflow-y: auto;
box-sizing: border-box;
}
/deep/ .works-detail-card {
margin-top: 12px;
overflow: hidden;
border: 1px solid #e9eff7;
border-radius: 12px;
background-color: #fff;
box-shadow: 0 7px 20px rgba(55, 85, 126, .08);
}
/deep/ .works-detail-scroll > .works-detail-card:first-child {
margin-top: 0;
}
/deep/ .works-detail-card__title {
position: relative;
margin: 0;
padding: 13px 14px 9px 22px;
color: #354255;
font-size: 15px;
line-height: 21px;
font-weight: 600;
}
/deep/ .works-detail-card__title::before {
position: absolute;
top: 16px;
bottom: 12px;
left: 12px;
width: 2px;
border-radius: 2px;
background-color: #1989fa;
content: "";
}
/deep/ .works-detail-card .van-cell {
padding: 10px 14px;
color: #596779;
}
/deep/ .works-detail-card .van-cell__title {
flex: 0 0 82px;
font-size: 14px;
}
/deep/ .works-detail-card .van-cell__value {
color: #4f5d70;
font-size: 14px;
word-break: break-word;
}
/deep/ .works-detail-description,
/deep/ .works-detail-files,
/deep/ .works-detail-empty {
padding: 4px 14px 16px;
color: #536174;
font-size: 14px;
line-height: 1.8;
white-space: pre-wrap;
word-break: break-word;
}
/deep/ .works-detail-empty {
color: #9aa7b8;
}
`,
data() {
return {
viewData: {},
visible: false,
historyLayerUnsubscribe: null,
}
},
methods: {
/**
* 查询并打开作品详情。
* row 需传作品 id;接口返回作品、活动、主题、类型及附件信息的 Result JSON。
*/
onOpen(row) {
this.$set(this, "viewData", Object.assign({}, row, {files: this.normalizeFiles(row.files)}))
window.h5HistoryLayerManager.open("works-collection-mine-info")
this.$axios.post("/platform/activity/worksCollection/common/findOne", {id: row.id}).then((res) => {
if (res.code === 0) {
this.viewData = res.data
try {
this.viewData.files = JSON.parse(this.viewData.files)
} catch (err) {
}
const viewData = res.data || {}
viewData.files = this.normalizeFiles(viewData.files)
this.$set(this, "viewData", viewData)
}
})
this.visible = true
},
normalizeFiles(files) {
if (Array.isArray(files)) return files
if (!files) return []
try {
const result = JSON.parse(files)
return Array.isArray(result) ? result : []
} catch (err) {
return []
}
},
closeInfo() {
window.h5HistoryLayerManager.close("works-collection-mine-info")
},
syncHistoryLayers(stack) {
this.$set(this, "visible", stack.includes("works-collection-mine-info"))
}
},
created() {
this.$set(this, "historyLayerUnsubscribe", window.h5HistoryLayerManager.subscribe((stack) => {
this.syncHistoryLayers(stack)
}))
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe()
}
}
@@ -1,66 +1,495 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app">
<!-- 导航栏 -->
<style>
body {
background-color: #f3f7fd;
}
<van-nav-bar title="作品阅览" left-text="返回" left-arrow placeholder
@click-left="historyBack" fixed></van-nav-bar>
<van-sticky offset-top="46px">
<van-search
:reverse-color="false"
:show-action="false"
@search="doSearch"
input-align="left"
placeholder="请输入姓名/工号搜索"
v-model="pageForm.searchKeyword"
></van-search>
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item v-model="pageForm.activityId" :options="activityOptions"
@change="doSearch"></van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.pageOrderName"
@change="doSearch">
<van-tab title="按上传时间正序排序" name="createdAt"></van-tab>
<van-tab title="按点赞多少倒序排序" name="num"></van-tab>
</van-tabs>
</van-sticky>
<table-list api="/platform/activity/worksCollection/read/h5/pageData"
:page_form.sync="pageForm"
ref="tableListRef"
title="name"
@ready="onReady"
>
<template v-slot="{index,row}">
<table-column label="主题类型">{{row.subjectName}}</table-column>
<table-column label="作品类型">{{row.worksName}}</table-column>
<table-column label="姓名">{{row.userName}}</table-column>
<table-column label="上传时间">{{$moment(row.createdAt).format('YYYY-MM-DD')}}</table-column>
<table-column label="点赞数">{{row.num}}</table-column>
<table-column label="附件">
<template v-if="row.files">
<file-preview :files="JSON.parse(row.files)" complete_result></file-preview>
</template>
</table-column>
.works-read-page {
min-height: 100vh;
padding-bottom: calc(24px + env(safe-area-inset-bottom));
box-sizing: border-box;
background-color: #f3f7fd;
}
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<span>查看</span>
.works-read-page .van-nav-bar {
background-color: #fff;
}
.works-read-page .van-nav-bar__title {
color: #172033;
font-size: 16px;
font-weight: 600;
}
.works-read-page .van-nav-bar .van-icon,
.works-read-page .van-nav-bar__text {
color: #1989fa;
}
.works-read-sticky {
padding: 22px 12px 1px;
box-sizing: border-box;
background-color: #f3f7fd;
}
.works-read-search {
margin: 0 0 8px;
padding: 0;
overflow: hidden;
border-radius: 12px;
background-color: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
}
.works-read-search .van-search__content {
height: 42px;
padding-left: 12px;
align-items: center;
border-radius: 12px;
background-color: #fff;
}
.works-read-search .van-field__control {
color: #263548;
font-size: 14px;
}
.works-read-search .van-field__control::placeholder {
color: #a7b0bf;
}
.works-read-filter {
margin: 0 0 10px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
transition: border-radius .18s ease;
}
.works-read-filter.is-open {
border-radius: 10px 10px 0 0;
}
.works-read-filter .van-dropdown-menu__bar {
height: 44px;
box-shadow: none;
}
.works-read-filter .van-dropdown-menu__item {
min-width: 0;
overflow: hidden;
}
.works-read-filter .van-dropdown-menu__title {
width: auto;
max-width: calc(100% - 24px);
padding: 0 12px 0 0;
overflow: hidden;
color: #66758a;
font-size: 14px;
text-overflow: ellipsis;
white-space: nowrap;
}
.works-read-filter .van-dropdown-menu__title::after {
right: 0;
}
.works-read-filter .van-dropdown-item__content {
width: calc(100% - 24px);
margin: 0 12px;
overflow: hidden;
border-radius: 0 0 12px 12px;
box-shadow: 0 10px 24px rgba(31, 65, 108, .14);
box-sizing: border-box;
}
.works-read-content {
padding: 0 12px;
}
.works-read-list .empty-state {
display: none;
}
.works-read-skeleton {
padding-top: 12px;
}
.works-read-skeleton__card {
margin-bottom: 12px;
padding: 14px;
overflow: hidden;
border: 1px solid #eef1f6;
border-radius: 12px;
background-color: #fff;
box-shadow: 0 7px 20px rgba(55, 85, 126, .08);
box-sizing: border-box;
}
.works-read-skeleton__card .van-skeleton {
padding: 0;
}
.works-read-skeleton__card .van-skeleton__title {
width: 42%;
height: 16px;
margin-bottom: 13px;
}
.works-read-skeleton__card .van-skeleton__row {
height: 12px;
margin-top: 10px;
}
.works-read-skeleton__card .van-skeleton__row:nth-child(2n) {
width: 72%;
}
.works-read-skeleton__card .van-skeleton__row:last-child {
width: 48%;
height: 28px;
margin-top: 16px;
border-radius: 14px;
}
.works-read-empty {
display: flex;
min-height: 280px;
padding: 46px 18px 36px;
align-items: center;
justify-content: center;
flex-direction: column;
box-sizing: border-box;
text-align: center;
}
.works-read-empty img {
display: block;
width: 86%;
max-width: 290px;
height: auto;
object-fit: contain;
}
.works-read-empty__title {
margin-top: 14px;
color: #50627a;
font-size: 15px;
line-height: 22px;
font-weight: 600;
}
.works-read-empty__hint {
margin-top: 5px;
color: #9aa7b8;
font-size: 12px;
line-height: 18px;
}
.works-read-card-header {
display: flex;
margin-bottom: 7px;
align-items: flex-start;
justify-content: space-between;
}
.works-read-card-heading {
min-width: 0;
padding-right: 10px;
flex: 1;
}
.works-read-card-title {
overflow: hidden;
color: #253247;
font-size: 15px;
line-height: 22px;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
.works-read-card-activity {
margin-top: 1px;
overflow: hidden;
color: #9ba6b6;
font-size: 12px;
line-height: 18px;
text-overflow: ellipsis;
white-space: nowrap;
}
.works-read-like-tag {
display: inline-flex;
min-height: 24px;
padding: 3px 9px;
align-items: center;
flex: none;
border: 0;
border-radius: 12px;
color: #397de0;
background-color: #edf5ff;
font-size: 12px;
line-height: 18px;
box-sizing: border-box;
}
.works-read-like-tag .van-icon {
margin-right: 4px;
font-size: 14px;
}
.works-read-files-row {
width: 100%;
min-width: 0;
margin-top: 8px;
padding-top: 10px;
border-top: 1px solid #edf1f6;
box-sizing: border-box;
}
.works-read-files-label {
display: flex;
margin-bottom: 7px;
align-items: center;
justify-content: space-between;
gap: 10px;
color: #98a3b3;
font-size: 12px;
line-height: 18px;
}
.works-read-files-hint {
flex: none;
color: #7893af;
font-size: 11px;
white-space: nowrap;
}
.works-read-files-scroll {
width: 100%;
min-width: 0;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.works-read-files-scroll::-webkit-scrollbar {
display: none;
}
.works-read-list .works-read-files-scroll .file-preview-container {
display: flex !important;
width: max-content;
min-width: 100%;
padding: 0;
grid-template-columns: none !important;
gap: 8px;
box-sizing: border-box;
}
.works-read-list .works-read-files-scroll .file-preview-container .file-item {
width: 88px;
height: 88px;
flex: 0 0 88px;
aspect-ratio: auto;
}
.works-read-action-icon {
font-size: 15px;
}
.works-read-like-action-wrap {
position: relative;
display: flex;
overflow: visible;
}
.works-read-like-hearts {
position: absolute;
z-index: 3;
bottom: 24px;
left: 50%;
width: 1px;
height: 1px;
pointer-events: none;
}
.works-read-like-heart {
position: absolute;
bottom: 0;
left: 0;
color: #ee3f4d;
font-size: 18px;
line-height: 1;
opacity: 0;
text-shadow: 0 3px 8px rgba(238, 63, 77, .32);
animation: works-read-heart-bubble 1s ease-out forwards;
}
.works-read-like-heart.is-heart-1 {
--heart-x: -26px;
--heart-y: -62px;
font-size: 15px;
animation-delay: 0s;
}
.works-read-like-heart.is-heart-2 {
--heart-x: -11px;
--heart-y: -82px;
font-size: 21px;
animation-delay: .08s;
}
.works-read-like-heart.is-heart-3 {
--heart-x: 7px;
--heart-y: -68px;
font-size: 14px;
animation-delay: .16s;
}
.works-read-like-heart.is-heart-4 {
--heart-x: 24px;
--heart-y: -88px;
font-size: 18px;
animation-delay: .24s;
}
.works-read-like-heart.is-heart-5 {
--heart-x: -18px;
--heart-y: -102px;
font-size: 13px;
animation-delay: .32s;
}
.works-read-like-heart.is-heart-6 {
--heart-x: 15px;
--heart-y: -108px;
font-size: 16px;
animation-delay: .4s;
}
@keyframes works-read-heart-bubble {
0% {
opacity: 0;
transform: translate(-50%, 5px) scale(.35);
}
20% {
opacity: 1;
transform: translate(-50%, -8px) scale(1.15);
}
62% {
opacity: 1;
transform: translate(-50%, -42px) scale(1);
}
100% {
opacity: 0;
transform: translate(calc(-50% + var(--heart-x)), var(--heart-y)) scale(.72);
}
}
@media (max-width: 350px) {
.works-read-content,
.works-read-sticky {
padding-right: 10px;
padding-left: 10px;
}
}
</style>
<div id="app" v-cloak>
<div class="works-read-page">
<van-nav-bar title="作品阅览" left-text="返回" left-arrow placeholder fixed
@click-left="handlePageBack"></van-nav-bar>
<van-sticky :offset-top="46">
<div class="works-read-sticky">
<van-search v-model="pageForm.searchKeyword" class="works-read-search"
placeholder="请输入姓名或工号搜索" shape="round" clearable
@search="doSearch" @clear="doSearch"></van-search>
<van-dropdown-menu class="works-read-filter" :class="{'is-open': filterOpened}" active-color="#1989fa">
<van-dropdown-item v-model="pageForm.activityId" :options="activityOptions" :multiple="false"
@change="doSearch" @open="onFilterOpen" @close="onFilterClose"></van-dropdown-item>
<van-dropdown-item v-model="pageForm.pageOrderName" :options="orderOptions" :multiple="false"
@change="doSearch" @open="onFilterOpen" @close="onFilterClose"></van-dropdown-item>
</van-dropdown-menu>
</div>
<template v-if="row.nbCount>0">
<div class="action-btn" @click="onLike(row)" v-if="!row.isThisLike">
<i class="fa fa-thumbs-o-up"></i>
<span>点赞</span>
</van-sticky>
<main class="works-read-content">
<div class="works-read-list">
<div v-if="skeletonLoading" class="works-read-skeleton">
<div v-for="index in 3" :key="'read-skeleton-' + index" class="works-read-skeleton__card">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<div class="action-btn" @click="onDeleteLike(row)" v-if="row.isThisLike">
<i class="fa fa-thumbs-up"></i>
<span>取消点赞</span>
<div v-if="!skeletonLoading && pageForm.totalCount === 0" class="works-read-empty">
<img src="/assets/mobile/img/activity/culture-empty.png" alt="暂无作品记录插画">
<div class="works-read-empty__title">暂无作品记录</div>
<div class="works-read-empty__hint">当前筛选条件下暂时没有可阅览作品</div>
</div>
</template>
</template>
</table-list>
<table-list api="/platform/activity/worksCollection/read/h5/pageData"
:page_form.sync="pageForm" ref="tableListRef" title="name" v-show="!skeletonLoading"
:show_loading_overlay="false" @ready="onReady" @loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="works-read-card-header">
<div class="works-read-card-heading">
<div class="works-read-card-title">{{row.name || '未命名作品'}}</div>
<div class="works-read-card-activity">{{row.activityName || '暂无活动信息'}}</div>
</div>
<div class="works-read-like-tag">
<van-icon name="good-job-o"></van-icon>{{row.num || 0}}
</div>
</div>
</template>
<template v-slot="{index,row}">
<table-column label="作品类型">{{row.worksName || '--'}}</table-column>
<table-column label="作者">{{row.userName || '--'}}</table-column>
<table-column label="工号">{{row.loginName || '--'}}</table-column>
<table-column label="上传时间">{{$moment(row.createdAt).format('YYYY-MM-DD')}}</table-column>
<div v-if="hasListFiles(row.files)" class="works-read-files-row">
<div class="works-read-files-label">
<span>上传附件</span>
<span v-if="getAttachmentCount(row.files) > 1" class="works-read-files-hint">
共{{getAttachmentCount(row.files)}}个附件 · 左滑查看更多
</span>
</div>
<div class="works-read-files-scroll">
<file-preview :files="row.files" complete_result></file-preview>
</div>
</div>
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<van-icon class="works-read-action-icon" name="eye-o"></van-icon>
<span>查看</span>
</div>
<div v-if="row.nbCount > 0" class="works-read-like-action-wrap">
<div class="action-btn" v-if="!row.isThisLike" @click="onLike(row)">
<van-icon class="works-read-action-icon" name="good-job-o"></van-icon>
<span>点赞</span>
</div>
<div class="action-btn delete" v-else @click="onDeleteLike(row)">
<van-icon class="works-read-action-icon" name="good-job"></van-icon>
<span>取消点赞</span>
</div>
<span v-if="likeHeartEffects[row.id]"
:key="'like-heart-' + row.id + '-' + (likeHeartEffectKeys[row.id] || 0)"
class="works-read-like-hearts" aria-hidden="true">
<span v-for="heartIndex in 6" :key="heartIndex"
class="works-read-like-heart" :class="'is-heart-' + heartIndex"></span>
</span>
</div>
</template>
</table-list>
</div>
</main>
</div>
<info ref="infoRef"></info>
</div>
@@ -69,6 +498,9 @@ layout("/layouts/platform_h5.html"){
new Vue({
el: "#app",
store,
components: {
'info': INFO,
},
data() {
return {
pageForm: {
@@ -77,79 +509,148 @@ layout("/layouts/platform_h5.html"){
totalCount: 0,
searchKeyword: '',
pageOrderName: 'createdAt',
pageOrderNameText: '',
activityId: '',
year: new Date().getFullYear(),
},
activityOptions: []
activityOptions: [],
orderOptions: [
{text: '按上传时间排序', value: 'createdAt'},
{text: '按点赞数量排序', value: 'num'},
],
filterOpened: false,
likeLoading: false,
likeHeartEffects: {},
likeHeartEffectKeys: {},
likeHeartEffectTimers: {},
skeletonLoading: true,
historyLayerPageKey: 'works-collection-read-h5',
}
},
components: {
'info': INFO,
},
methods: {
// state.initial 表示查询前列表为空,仅在首次或重置查询时显示整组卡片骨架。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, 'skeletonLoading', state.loading)
},
// 判断列表附件是否包含有效记录;files 支持上传组件保存的 JSON 字符串或数组。
hasListFiles(files) {
if (Array.isArray(files)) return files.length > 0
if (!files) return false
try {
const fileList = JSON.parse(files)
return Array.isArray(fileList) && fileList.length > 0
} catch (err) {
return false
}
},
// 获取当前作品的全部附件数量;files 支持 JSON 字符串或数组,返回 Number 类型。
getAttachmentCount(files) {
if (Array.isArray(files)) return files.length
if (!files) return 0
try {
const fileList = JSON.parse(files)
return Array.isArray(fileList) ? fileList.length : 0
} catch (err) {
return 0
}
},
onLike(row) {
this.$dialog.confirm({
title: '温馨提示',
message: '您确定要点赞吗?',
}).then(() => {
this.$axios.post("/platform/activity/worksCollection/read/h5/doLike", {uploadId: row.id}).then((res) => {
if (res.code === 0) {
row.num = row.num + 1
row.isThisLike = true
this.$toast.success(res.msg)
}
})
}).catch(() => {
// on cancel
});
this.executeLikeAction(row, 'like')
},
onDeleteLike(row) {
this.$dialog.confirm({
title: '温馨提示',
message: '您确定要取消点赞吗?',
}).then(() => {
this.$axios.post("/platform/activity/worksCollection/read/h5/doDeleteLike", {uploadId: row.id}).then((res) => {
if (res.code === 0) {
row.num = row.num - 1
row.isThisLike = false
this.$toast.success(res.msg)
}
})
}).catch(() => {
// on cancel
});
this.executeLikeAction(row, 'cancel')
},
/**
* 执行作品点赞或取消点赞。
* row 需包含作品 idaction 传 like 或 cancel;接口返回 Result JSON,方法无返回值。
*/
executeLikeAction(row, action) {
if (!row || !row.id || this.likeLoading) return
const isCancel = action === 'cancel'
const url = isCancel
? '/platform/activity/worksCollection/read/h5/doDeleteLike'
: '/platform/activity/worksCollection/read/h5/doLike'
this.$set(this, 'likeLoading', true)
this.$axios.post(url, {uploadId: row.id}).then((res) => {
if (res.code === 0) {
const currentNum = Number(row.num) || 0
this.$set(row, 'num', isCancel ? Math.max(currentNum - 1, 0) : currentNum + 1)
this.$set(row, 'isThisLike', !isCancel)
if (!isCancel) this.playLikeHeart(row.id)
}
}).finally(() => {
this.$set(this, 'likeLoading', false)
})
},
// 点赞成功后为当前作品播放多颗红色爱心错峰上浮动画,重复触发时重新开始计时。
playLikeHeart(rowId) {
if (!rowId) return
if (this.likeHeartEffectTimers[rowId]) {
clearTimeout(this.likeHeartEffectTimers[rowId])
}
this.$set(this.likeHeartEffects, rowId, false)
this.$set(this.likeHeartEffectKeys, rowId, (this.likeHeartEffectKeys[rowId] || 0) + 1)
this.$nextTick(() => {
this.$set(this.likeHeartEffects, rowId, true)
const timer = setTimeout(() => {
this.$set(this.likeHeartEffects, rowId, false)
this.$delete(this.likeHeartEffectTimers, rowId)
}, 1400)
this.$set(this.likeHeartEffectTimers, rowId, timer)
})
},
onView(row) {
this.$refs.infoRef.onOpen(row)
},
async listActivity() {
const res = await this.$axios.post("/platform/activity/worksCollection/common/listActivity")
if (res.code === 0) {
res.data.forEach(v => {
v.text = v.name
v.value = v.id
})
this.activityOptions = res.data
if (this.activityOptions.length > 0) {
this.pageForm.activityId = this.activityOptions[0].id
}
}
onFilterOpen() {
this.$set(this, 'filterOpened', true)
},
async onReady() {
await this.listActivity()
this.doSearch()
onFilterClose() {
this.$set(this, 'filterOpened', false)
},
handlePageBack() {
if (window.h5HistoryLayerManager.stack.length) {
window.h5HistoryLayerManager.close()
return
}
this.historyBack()
},
// 活动接口接收年度,返回 id、name 组成的活动列表;完成后触发首次作品查询。
listActivity() {
return this.$axios.post('/platform/activity/worksCollection/common/listActivity', {
year: this.pageForm.year
}, {
h5SkeletonLoading: true
}).then((res) => {
if (res.code === 0) {
const options = (res.data || []).map((item) => {
return {text: item.name, value: item.id}
})
this.$set(this, 'activityOptions', options)
this.$set(this.pageForm, 'activityId', options.length > 0 ? options[0].value : '')
}
})
},
onReady() {
this.listActivity().then(() => {
this.doSearch()
})
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$set(this.pageForm, 'pageNumber', 1)
this.$set(this.pageForm, 'totalCount', 0)
this.$refs.tableListRef.doSearch()
})
},
}
},
created() {
window.h5HistoryLayerManager.register(this.historyLayerPageKey, null)
},
beforeDestroy() {
Object.keys(this.likeHeartEffectTimers).forEach((rowId) => {
clearTimeout(this.likeHeartEffectTimers[rowId])
})
window.h5HistoryLayerManager.unregister(this.historyLayerPageKey)
}
})
</script>
@@ -3,189 +3,539 @@ const applyForm = {
/*language=HTML*/
`
<div>
<van-action-sheet v-model="visible" :style="{ 'background-color': '#F7F8FA' }" title="报名信息">
<van-form ref="formRef" class="form-container">
<van-cell-group title="基础信息" class="form-section">
<van-field label="姓名" readonly v-model="formData.username"></van-field>
<van-field label="工号" readonly v-model="formData.loginname"></van-field>
<van-field label="所在单位" readonly v-model="formData.unitName"></van-field>
<van-field label="所属工会" readonly v-model="formData.unionName"></van-field>
<van-field label="性别" readonly v-model="formData.sex"></van-field>
</van-cell-group>
<van-cell-group title="报名信息" class="form-section">
<van-field label="主题类型"
required
:rules="[{ required: true, message: '请选择主题类型' }]"
readonly
is-link
@click="showSubjectPicker = true"
placeholder="请选择主题类型"
name="subjectName"
v-model="formData.subjectName">
</van-field>
<van-popup v-model="showSubjectPicker" position="bottom">
<van-picker
show-toolbar
:columns="subjectTypesOptions.map(v=>v.typeName)"
@confirm="onSubjectConfirm"
@cancel="showSubjectPicker=false"
></van-picker>
</van-popup>
<van-popup class="works-apply-popup" :style="{height: '100%', width: '100%'}" position="right"
safe-area-inset-bottom :close-on-click-overlay="false" v-model="visible">
<div class="works-apply-page">
<van-sticky>
<van-nav-bar title="作品提交" left-text="返回" left-arrow @click-left="closeApply"></van-nav-bar>
</van-sticky>
<van-form ref="formRef" class="works-apply-form form-container">
<div class="works-apply-scroll">
<section class="works-apply-section">
<h2 class="works-apply-section__title">基础信息</h2>
<van-cell-group :border="false">
<van-field label="姓名" readonly v-model="formData.username"></van-field>
<van-field label="工号" readonly v-model="formData.loginname"></van-field>
<van-field label="所在单位" readonly v-model="formData.unitName"></van-field>
<van-field label="所属工会" readonly v-model="formData.unionName"></van-field>
<van-field label="性别" readonly v-model="formData.sex"></van-field>
</van-cell-group>
</section>
<van-field label="作品类型"
required
:rules="[{ required: true, message: '请选择作品类型' }]"
readonly
is-link
@click="showWorksPicker = true"
placeholder="请选择作品类型"
name="worksName"
v-model="formData.worksName">
</van-field>
<van-popup v-model="showWorksPicker" position="bottom">
<van-picker
show-toolbar
:columns="worksTypeOptions.map(v=>v.worksTypeName)"
@confirm="onWorksConfirm"
@cancel="showWorksPicker=false"
></van-picker>
</van-popup>
<section class="works-apply-section">
<h2 class="works-apply-section__title">作品信息</h2>
<van-cell-group :border="false">
<van-field label="主题类型" required readonly is-link name="subjectName"
placeholder="请选择主题类型" :rules="[{required: true, message: '请选择主题类型'}]"
v-model="formData.subjectName" @click="openSubjectPicker"></van-field>
<van-field label="作品类型" required readonly is-link name="worksName"
placeholder="请先选择主题类型" :rules="[{required: true, message: '请选择作品类型'}]"
v-model="formData.worksName" @click="openWorksPicker"></van-field>
<van-field label="作品名称" required name="name" maxlength="100" show-word-limit
placeholder="请填写作品名称" :rules="[{required: true, message: '请填写作品名称'}]"
v-model="formData.name"></van-field>
<van-field class="more-text" label="作品描述" required type="textarea" name="description"
rows="4" autosize show-word-limit :maxlength="descriptionMaxLength"
placeholder="请填写作品描述" :rules="[{required: true, message: '请填写作品描述'}]"
v-model="formData.description"></van-field>
</van-cell-group>
</section>
<van-field label="作品名称"
required
v-model="formData.name"
name="name"
placeholder="请填写作品名称"
:rules="[{ required: true, message: '请填写作品名称' }]"></van-field>
<van-field label="作品描述"
v-model="formData.description"
type="textarea"
name="description"
rows="4"
autosize
maxlength="150"
:rules="[{ required: true, message: '请填写作品描述' }]"
class="more-text"
placeholder="请填写作品描述"></van-field>
</van-cell-group>
<van-cell-group title="附件">
<van-field class="more-text"
name="files"
:rules="[{ required: true,message:'请上传附件' }]"
label=""
required>
<template #input>
<h5-file-upload
slot="input"
:value.sync="formData.files"
:upload_number="chooseWorksType && chooseWorksType.allowFileNum"
upload_mode="file"
upload_result_category="array"
upload_result_type="url"
complete_result
:accept="fileAccept"
></h5-file-upload>
</template>
</van-field>
</van-cell-group>
<section class="works-apply-section">
<h2 class="works-apply-section__title">作品附件</h2>
<div class="works-apply-section__hint">附件说明:请根据所选作品类型上传符合要求的文件</div>
<van-cell-group :border="false">
<van-field class="more-text works-apply-upload-field" name="files" label="" required
:rules="[{required: true, message: '请上传附件'}]">
<template #input>
<h5-file-upload slot="input" :value.sync="formData.files"
:upload_number="chooseWorksType && chooseWorksType.allowFileNum"
upload_mode="file" upload_result_category="array"
upload_result_type="url" complete_result :accept="fileAccept">
</h5-file-upload>
</template>
</van-field>
</van-cell-group>
</section>
</div>
<div class="works-apply-footer">
<van-button type="primary" block round :loading="formLoading" loading-text="提交中..."
@click="onSubmit">提交作品</van-button>
</div>
</van-form>
</div>
</van-popup>
<div class="form-actions">
<van-button @click="onSubmit" round type="info">提交</van-button>
</div>
</van-form>
</van-action-sheet>
<van-popup class="works-option-popup" v-model="showOptionPicker" position="bottom" round
:close-on-click-overlay="false">
<div class="works-option-popup__header">
<button type="button" class="works-option-popup__action" @click="cancelOptionPopup">取消</button>
<div class="works-option-popup__title">{{optionPopupTitle}}</div>
<button type="button" class="works-option-popup__action"
:disabled="pendingOptionValue === null || pendingOptionValue === undefined || pendingOptionValue === ''"
@click="confirmOptionPopup">确认</button>
</div>
<div class="works-option-popup__list">
<div v-if="optionPopupOptions.length === 0" class="works-option-popup__empty">暂无可选内容</div>
<button v-for="option in optionPopupOptions" :key="option.value" type="button"
class="works-option-popup__option"
:class="{'is-selected': pendingOptionValue === option.value}"
:aria-pressed="pendingOptionValue === option.value"
@click="selectOptionPopupItem(option)">
<span class="works-option-popup__option-text">{{option.text}}</span>
</button>
</div>
</van-popup>
<van-dialog class="works-apply-confirm" title="提交确认" message="确认提交当前作品信息吗?"
:value="submitConfirmVisible" :close-on-click-overlay="false" show-cancel-button
cancel-button-text="取消" confirm-button-text="确认提交"
@cancel="closeSubmitConfirm" @confirm="confirmSubmit"></van-dialog>
</div>
`,
style: /*language=CSS*/ `
/deep/ .works-apply-popup {
background-color: #f3f7fd;
}
/deep/ .works-apply-page {
position: relative;
min-height: 100vh;
background-color: #f3f7fd;
}
/deep/ .works-apply-page .van-nav-bar {
background-color: #fff;
}
/deep/ .works-apply-page .van-nav-bar__title {
color: #1f2937;
font-size: 16px;
}
/deep/ .works-apply-page .van-nav-bar .van-icon,
/deep/ .works-apply-page .van-nav-bar__text {
color: #397fbd;
}
/deep/ .works-apply-form.form-container {
min-height: 100%;
padding: 0;
box-sizing: border-box;
background: #f3f7fc;
}
/deep/ .works-apply-scroll {
height: calc(100vh - 46px);
padding: 12px 15px calc(84px + env(safe-area-inset-bottom));
overflow-x: hidden;
overflow-y: auto;
box-sizing: border-box;
background: #f3f7fc;
}
/deep/ .works-apply-section {
margin-bottom: 11px;
overflow: hidden;
border: 1px solid #e9eff7;
border-radius: 10px;
background: #fff;
box-shadow: 0 3px 11px rgba(51, 87, 126, .08);
}
/deep/ .works-apply-section__title {
position: relative;
margin: 0;
padding: 12px 14px 8px 21px;
color: #354255;
font-size: 15px;
line-height: 20px;
font-weight: 600;
background: transparent;
}
/deep/ .works-apply-section__title::before {
position: absolute;
top: 15px;
bottom: 11px;
left: 12px;
width: 2px;
border-radius: 2px;
background: #1989fa;
content: "";
}
/deep/ .works-apply-section__hint {
padding: 0 14px 8px 21px;
color: orangered;
font-size: 14px;
line-height: 18px;
font-weight: normal;
}
/deep/ .works-apply-section .van-cell-group {
margin: 0;
background: transparent;
}
/deep/ .works-apply-section .van-cell {
min-height: 44px;
padding: 10px 14px;
background: transparent;
}
/deep/ .works-apply-section .van-cell:not(:last-child)::after {
right: 14px;
left: 14px;
border-color: #eef2f7;
}
/deep/ .works-apply-section .van-field__label {
width: 84px;
color: #596779;
font-size: 14px;
line-height: 24px;
}
/deep/ .works-apply-section .van-field__value,
/deep/ .works-apply-section .van-field__control {
color: #4f5d70;
font-size: 14px;
line-height: 24px;
}
/deep/ .works-apply-section .van-field__control::placeholder {
color: #b1bac6;
}
/deep/ .works-apply-section .van-cell--required::before {
left: 6px;
color: #f06464;
}
/deep/ .works-apply-upload-field .van-field__value {
overflow: visible;
}
/deep/ .works-apply-footer {
position: fixed;
z-index: 100;
right: 0;
bottom: 0;
left: 0;
display: flex;
padding: 10px 18px calc(10px + env(safe-area-inset-bottom));
border-top: 1px solid #edf1f6;
background: rgba(255, 255, 255, .98);
box-shadow: 0 -4px 14px rgba(50, 78, 112, .06);
}
/deep/ .works-apply-footer .van-button {
flex: 1;
height: 40px;
margin: 0;
border-color: #1989fa;
border-radius: 14px !important;
font-size: 16px;
background: #1989fa;
}
/deep/ .works-option-popup {
display: flex;
height: 50%;
padding-bottom: env(safe-area-inset-bottom);
flex-direction: column;
box-sizing: border-box;
background: #f5f7fa;
}
/deep/ .works-option-popup__header {
display: flex;
min-height: 52px;
padding: 0 16px;
align-items: center;
justify-content: space-between;
flex: none;
border-bottom: 1px solid #edf1f6;
background: #fff;
}
/deep/ .works-option-popup__title {
color: #263548;
font-size: 17px;
line-height: 24px;
font-weight: 600;
}
/deep/ .works-option-popup__action {
min-width: 48px;
padding: 8px 0;
border: 0;
color: #1989fa;
font-size: 15px;
line-height: 22px;
text-align: left;
background: transparent;
}
/deep/ .works-option-popup__action:last-child {
text-align: right;
}
/deep/ .works-option-popup__action:disabled {
color: #b7c0cc;
}
/deep/ .works-option-popup__list {
min-height: 0;
padding: 12px;
overflow-y: auto;
flex: 1;
-webkit-overflow-scrolling: touch;
}
/deep/ .works-option-popup__empty {
padding: 44px 18px;
color: #98a4b3;
font-size: 14px;
line-height: 22px;
text-align: center;
}
/deep/ .works-option-popup__option {
display: flex;
width: 100%;
min-height: 58px;
margin-bottom: 10px;
padding: 12px 14px;
align-items: center;
border: 1px solid #e3e9f1;
border-radius: 10px;
color: #536174;
text-align: left;
background: #fff;
}
/deep/ .works-option-popup__option:last-child {
margin-bottom: 0;
}
/deep/ .works-option-popup__option.is-selected {
border-color: #1989fa;
color: #176fbd;
background: #eef7ff;
box-shadow: 0 3px 10px rgba(25, 137, 250, .1);
}
/deep/ .works-option-popup__option-text {
min-width: 0;
flex: 1;
font-size: 15px;
line-height: 23px;
word-break: break-word;
overflow-wrap: anywhere;
}
/deep/ .works-apply-confirm .van-dialog__header {
color: #263548;
font-size: 17px;
font-weight: 600;
}
/deep/ .works-apply-confirm .van-dialog__message {
padding: 24px 20px;
color: #536174;
font-size: 15px;
line-height: 24px;
text-align: center;
}
`,
data() {
return {
row: {},
visible: false,
formData: {},
subjectTypesOptions: [],
showSubjectPicker: false,
worksTypeOptions: [],
showWorksPicker: false,
fileAccept: null,
chooseWorksType: {}
row: {}, visible: false, formData: {}, formLoading: false,
subjectTypesOptions: [], worksTypeOptions: [],
showOptionPicker: false, optionPopupType: "", optionPopupTitle: "",
optionPopupOptions: [], pendingOptionValue: "",
submitConfirmVisible: false, fileAccept: null, chooseWorksType: {},
historyLayerPageKey: "works-collection-upload-h5",
historyLayerUnsubscribe: null, ownsHistoryRegistration: false
}
},
computed: {
descriptionMaxLength() {
return this.chooseWorksType && this.chooseWorksType.maxWordCount ? this.chooseWorksType.maxWordCount : 150
}
},
methods: {
async onOpen(row) {
this.$set(this.formData, 'username', this.$store.state.user.username)
this.$set(this.formData, 'loginname', this.$store.state.user.loginname)
this.$set(this.formData, 'unionName', this.$store.state.user.union.name)
this.$set(this.formData, 'unitName', this.$store.state.user.unit.name)
this.$set(this.formData, 'sex', this.$store.state.user.sex)
this.$set(this.formData, 'activityId', row.id)
await this.activityChange(row.id)
this.row = row
this.visible = true
resetFormData() {
const user = this.$store.state.user || {}
const union = user.union || {}
const unit = user.unit || {}
this.$set(this, "formData", {})
this.$set(this.formData, "username", user.username)
this.$set(this.formData, "loginname", user.loginname)
this.$set(this.formData, "unionName", union.name)
this.$set(this.formData, "unitName", unit.name)
this.$set(this.formData, "sex", user.sex)
this.$set(this.formData, "files", [])
this.$set(this, "subjectTypesOptions", [])
this.$set(this, "worksTypeOptions", [])
this.$set(this, "chooseWorksType", {})
this.$set(this, "fileAccept", null)
},
async onOpenEdit(row) {
onOpen(row) {
this.resetFormData()
this.$set(this, "row", row)
this.$set(this.formData, "activityId", row.id)
this.activityChange(row.id).then(() => {
window.h5HistoryLayerManager.open("works-collection-apply")
})
},
onOpenEdit(row) {
const formData = clone(row)
formData.files = JSON.parse(formData.files)
await this.activityChange(formData.activityId)
await this.subjectChange(formData.subjectId)
this.row = formData
this.formData = formData
this.$set(this.formData, 'username', this.$store.state.user.username)
this.$set(this.formData, 'loginname', this.$store.state.user.loginname)
this.$set(this.formData, 'sex', this.$store.state.user.sex)
this.chooseWorksType = this.worksTypeOptions.find((o) => o.id === formData.worksId)
if (this.chooseWorksType.allowFileTypes && this.chooseWorksType.allowFileTypes.length > 0) {
this.fileAccept = this.chooseWorksType.allowFileTypes.map((item) => "." + item).join(",")
}
this.visible = true
formData.files = this.normalizeFiles(formData.files)
this.resetFormData()
this.$set(this, "row", formData)
this.$set(this, "formData", formData)
const user = this.$store.state.user || {}
this.$set(this.formData, "username", user.username)
this.$set(this.formData, "loginname", user.loginname)
this.$set(this.formData, "sex", user.sex)
this.activityChange(formData.activityId).then(() => {
// 编辑时以主题主键匹配当前活动配置,避免列表未携带主题名称时无法回显。
const subjectType = this.subjectTypesOptions.find((item) => item.id === formData.subjectId)
if (subjectType) this.$set(this.formData, "subjectName", subjectType.typeName)
return this.subjectChange(formData.subjectId)
}).then(() => {
this.setSelectedWorksType(formData.worksId)
window.h5HistoryLayerManager.open("works-collection-apply")
})
},
onWorksConfirm(value, index) {
this.$set(this.formData, 'worksName', value)
this.$set(this.formData, 'worksId', this.worksTypeOptions[index].id)
this.showWorksPicker = false
this.chooseWorksType = this.worksTypeOptions.find((o) => o.id === this.worksTypeOptions[index].id)
if (this.chooseWorksType.allowFileTypes && this.chooseWorksType.allowFileTypes.length > 0) {
this.fileAccept = this.chooseWorksType.allowFileTypes.map((item) => "." + item).join(",")
// 附件可能由列表 SQL 返回 JSON 字符串,也可能已被序列化为数组;统一转换为上传组件需要的数组。
normalizeFiles(files) {
if (Array.isArray(files)) return files
if (!files) return []
try {
const result = JSON.parse(files)
return Array.isArray(result) ? result : []
} catch (err) {
return []
}
},
async onSubjectConfirm(value, index) {
this.$set(this.formData, 'subjectName', value)
this.$set(this.formData, 'subjectId', this.subjectTypesOptions[index].id)
await this.subjectChange(this.subjectTypesOptions[index].id)
this.showSubjectPicker = false
closeApply() { window.h5HistoryLayerManager.close("works-collection-apply") },
// 打开统一选择弹框:主题选项由活动配置提供,确认后再写入表单。
openSubjectPicker() {
this.$set(this, "optionPopupType", "subject")
this.$set(this, "optionPopupTitle", "选择主题类型")
this.$set(this, "optionPopupOptions", this.subjectTypesOptions.map((item) => {
return {value: item.id, text: item.typeName, source: item}
}))
this.$set(this, "pendingOptionValue", this.formData.subjectId || "")
window.h5HistoryLayerManager.open("works-collection-option-picker")
},
openWorksPicker() {
if (!this.formData.subjectId) {
this.$toast("请先选择主题类型")
return
}
this.$set(this, "optionPopupType", "works")
this.$set(this, "optionPopupTitle", "选择作品类型")
this.$set(this, "optionPopupOptions", this.worksTypeOptions.map((item) => {
return {value: item.id, text: item.worksTypeName, source: item}
}))
this.$set(this, "pendingOptionValue", this.formData.worksId || "")
window.h5HistoryLayerManager.open("works-collection-option-picker")
},
cancelOptionPopup() { window.h5HistoryLayerManager.close("works-collection-option-picker") },
selectOptionPopupItem(option) { this.$set(this, "pendingOptionValue", option.value) },
/**
* 确认类型选项并回填作品表单。
* optionPopupType 为 subject 或 workspendingOptionValue 为对应业务主键;方法无返回值。
*/
confirmOptionPopup() {
const selectedOption = this.optionPopupOptions.find((item) => {
return item.value === this.pendingOptionValue
})
if (!selectedOption) return
if (this.optionPopupType === "subject") {
this.onSubjectConfirm(selectedOption)
return
}
this.onWorksConfirm(selectedOption)
},
onWorksConfirm(selectedOption) {
this.$set(this.formData, "worksName", selectedOption.text)
this.$set(this.formData, "worksId", selectedOption.value)
this.setSelectedWorksType(selectedOption.value)
this.cancelOptionPopup()
},
onSubjectConfirm(selectedOption) {
this.$set(this.formData, "subjectName", selectedOption.text)
this.$set(this.formData, "subjectId", selectedOption.value)
this.$set(this.formData, "worksName", "")
this.$set(this.formData, "worksId", "")
this.$set(this.formData, "files", [])
this.$set(this, "chooseWorksType", {})
this.$set(this, "fileAccept", null)
this.subjectChange(selectedOption.value).then(() => { this.cancelOptionPopup() })
},
setSelectedWorksType(worksId) {
const worksType = this.worksTypeOptions.find((item) => item.id === worksId) || {}
this.$set(this, "chooseWorksType", worksType)
if (worksType.allowFileTypes && worksType.allowFileTypes.length > 0) {
this.$set(this, "fileAccept", worksType.allowFileTypes.map((item) => "." + item).join(","))
return
}
this.$set(this, "fileAccept", null)
},
onSubmit() {
this.$refs.formRef.validate().then(() => {
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
}).then(() => {
this.$toast.loading({
message: '提交中...',
forbidClick: true,
});
this.$axios.post("/platform/activity/worksCollection/upload" + (this.formData.id ? "/update" : "/insert"), {data: JSON.stringify(this.formData)}).then((res) => {
if (res.code === 0) {
this.$toast.clear();
this.$toast.success("提交成功")
pjaxReplace("/platform/activity/worksCollection/mine/h5")
}
})
})
}).catch();
window.h5HistoryLayerManager.open("works-collection-submit-confirm")
}).catch(() => {
// 表单校验失败时由字段规则直接展示提示,不进入提交确认。
})
},
async activityChange(value) {
const resp = await this.$axios.post("/platform/activity/worksCollection/common/getSubjectTypes", {activityId: value})
if (resp.code === 0) {
this.subjectTypesOptions = resp.data
}
closeSubmitConfirm() { window.h5HistoryLayerManager.close("works-collection-submit-confirm") },
confirmSubmit() {
window.h5HistoryLayerManager.close("works-collection-submit-confirm", () => { this.submitForm() })
},
async subjectChange(value) {
const resp = await this.$axios.post("/platform/activity/worksCollection/common/getWorksTypes", {subjectId: value})
if (resp.code === 0) {
this.worksTypeOptions = resp.data
}
submitForm() {
this.$set(this, "formLoading", true)
this.$axios.post("/platform/activity/worksCollection/upload" + (this.formData.id ? "/update" : "/insert"), {
data: JSON.stringify(this.formData)
}).then((res) => {
if (res.code === 0) {
this.$toast.success("提交成功")
window.h5HistoryLayerManager.closeAllAndNavigate("/platform/activity/worksCollection/mine/h5")
}
}).finally(() => { this.$set(this, "formLoading", false) })
},
activityChange(value) {
return this.$axios.post("/platform/activity/worksCollection/common/getSubjectTypes", {activityId: value}).then((res) => {
if (res.code === 0) this.$set(this, "subjectTypesOptions", res.data || [])
})
},
subjectChange(value) {
return this.$axios.post("/platform/activity/worksCollection/common/getWorksTypes", {subjectId: value}).then((res) => {
if (res.code === 0) this.$set(this, "worksTypeOptions", res.data || [])
})
},
syncHistoryLayers(stack) {
this.$set(this, "visible", stack.includes("works-collection-apply"))
this.$set(this, "showOptionPicker", stack.includes("works-collection-option-picker"))
this.$set(this, "submitConfirmVisible", stack.includes("works-collection-submit-confirm"))
}
},
created() {
if (!window.h5HistoryLayerManager.pageKey) {
window.h5HistoryLayerManager.ensureRegistered(this.historyLayerPageKey)
this.$set(this, "ownsHistoryRegistration", true)
}
this.$set(this, "historyLayerUnsubscribe", window.h5HistoryLayerManager.subscribe((stack) => {
this.syncHistoryLayers(stack)
}))
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) this.historyLayerUnsubscribe()
if (this.ownsHistoryRegistration) window.h5HistoryLayerManager.unregister(this.historyLayerPageKey)
}
}
@@ -1,108 +1,690 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style scoped>
.info-title {
font-weight: bold;
background-color: white;
padding: 13px;
box-shadow: 0 8px 12px #ebedf0;
text-align: center !important;
<style>
body {
background-color: #f3f7fd;
}
.info-container {
height: calc(100vh - 46px - 44px);
min-height: calc(100vh - 46px - 44px);
overflow-y: auto;
.works-collection-page {
min-height: 100vh;
padding-bottom: calc(24px + env(safe-area-inset-bottom));
box-sizing: border-box;
background-color: #f3f7fd;
}
.info-img {
display: block;
.works-collection-page .van-nav-bar {
background-color: #fff;
}
.van-count-down {
.works-collection-page .van-nav-bar__title {
color: #1f2937;
font-size: 16px;
}
.works-collection-page .van-nav-bar .van-icon,
.works-collection-page .van-nav-bar__text {
color: #397fbd;
}
.works-collection-content {
padding: 12px 12px 0;
}
.works-collection-hero {
position: relative;
height: 134px;
overflow: hidden;
border-radius: 10px;
color: #fff;
background-color: #247fe8;
box-shadow: 0 8px 20px rgba(51, 117, 219, .18);
}
.works-collection-hero__image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
.works-collection-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%);
}
.works-collection-hero__title {
font-size: 25px;
line-height: 34px;
font-weight: 700;
letter-spacing: 1px;
text-shadow: 0 2px 6px rgba(17, 86, 182, .18);
}
.works-collection-hero__subtitle {
margin-top: 4px;
font-size: 13px;
line-height: 20px;
letter-spacing: .5px;
opacity: .95;
}
.works-collection-sticky {
padding: 10px 0 1px;
background-color: #f3f7fd;
}
.van-sticky--fixed .works-collection-sticky {
padding-right: 12px;
padding-left: 12px;
box-sizing: border-box;
}
.works-collection-search {
margin: 0 0 8px;
padding: 0;
overflow: hidden;
border-radius: 12px;
background-color: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
}
.works-collection-search .van-search__content {
height: 42px;
padding-left: 12px;
align-items: center;
border-radius: 12px;
background-color: #fff;
}
.works-collection-search .van-field__control {
color: #263548;
font-size: 14px;
}
.works-collection-search .van-field__control::placeholder {
color: #a7b0bf;
}
.works-collection-filter {
margin: 0 0 10px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
transition: border-radius .18s ease;
}
.works-collection-filter.is-open {
border-radius: 10px 10px 0 0;
}
.works-collection-filter .van-dropdown-menu__bar {
height: 44px;
box-shadow: none;
}
.works-collection-filter .van-dropdown-menu__item {
min-width: 0;
overflow: hidden;
}
.works-collection-filter .van-dropdown-menu__title {
width: auto;
max-width: calc(100% - 24px);
padding: 0 12px 0 0;
overflow: hidden;
color: #66758a;
font-size: 14px;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
box-sizing: border-box;
}
.works-collection-filter .van-dropdown-menu__title::after {
right: 0;
}
.works-collection-filter .van-dropdown-menu__title--active,
.works-collection-filter .van-dropdown-item__option--active,
.works-collection-filter .van-dropdown-item__option--active .van-dropdown-item__icon {
color: #1989fa;
}
.works-collection-filter .van-dropdown-menu__title--down::after {
border-color: transparent transparent #1989fa #1989fa;
}
.works-collection-filter .van-dropdown-item__content {
width: calc(100% - 24px);
margin: 0 12px;
overflow: hidden;
box-sizing: border-box;
border-radius: 0 0 12px 12px;
box-shadow: 0 10px 24px rgba(31, 65, 108, .14);
}
.works-collection-filter .van-dropdown-item__option {
min-height: 48px;
padding: 0 16px;
color: #46566c;
font-size: 14px;
}
.works-collection-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.works-collection-card {
position: relative;
overflow: hidden;
border-radius: 10px;
background-color: #fff;
box-shadow: 0 7px 20px rgba(55, 85, 126, .08);
cursor: pointer;
}
.works-collection-card__cover,
.works-collection-card__cover-state {
display: flex;
width: 100%;
height: 150px;
align-items: center;
justify-content: center;
box-sizing: border-box;
background-color: #edf4fc;
}
.works-collection-card__cover {
display: block;
object-fit: cover;
}
.works-collection-card__cover-state {
flex-direction: column;
row-gap: 8px;
color: #8da1ba;
font-size: 13px;
}
.works-collection-card__cover-state .van-icon {
font-size: 34px;
}
.works-collection-card__cover-state.is-error {
color: #c27b75;
background-color: #fff3f1;
}
.works-collection-card__body {
padding: 12px 38px 14px 14px;
}
.works-collection-card__title {
min-width: 0;
overflow: hidden;
color: #303846;
font-size: 14px;
line-height: 21px;
text-overflow: ellipsis;
white-space: nowrap;
}
.works-collection-card__time {
margin-top: 4px;
overflow: hidden;
color: #98a2b2;
font-size: 12px;
line-height: 17px;
text-overflow: ellipsis;
white-space: nowrap;
}
.works-collection-card__meta {
display: flex;
margin-top: 10px;
padding-top: 9px;
flex-wrap: wrap;
gap: 6px;
border-top: 1px solid #edf1f6;
}
.works-collection-card__tag {
display: inline-flex;
padding: 3px 8px;
align-items: center;
border-radius: 4px;
color: #397de0;
background-color: #edf5ff;
font-size: 11px;
line-height: 16px;
}
.works-collection-card__tag-label {
margin-right: 4px;
color: #8aa7cf;
}
.works-collection-card__arrow {
position: absolute;
top: 164px;
right: 14px;
color: #b4bdca;
font-size: 16px;
}
.works-collection-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;
}
.works-collection-empty img {
display: block;
width: 82%;
max-width: 280px;
height: auto;
object-fit: contain;
}
.works-collection-empty__title {
margin-top: 14px;
color: #50627a;
font-size: 15px;
line-height: 22px;
font-weight: 600;
}
.works-collection-empty__hint {
margin-top: 5px;
color: #9aa7b8;
font-size: 12px;
line-height: 18px;
}
.works-collection-page .van-list__loading {
color: #9aa7b8;
font-size: 13px;
line-height: 44px;
}
.works-collection-skeleton {
display: flex;
flex-direction: column;
gap: 10px;
}
.works-collection-skeleton__card {
overflow: hidden;
border-radius: 10px;
background-color: #fff;
box-shadow: 0 7px 20px rgba(55, 85, 126, .08);
}
.works-collection-skeleton__cover,
.works-collection-skeleton__body .van-skeleton {
padding: 0;
}
.works-collection-skeleton__cover .van-skeleton__row {
width: 100% !important;
height: 150px;
margin: 0;
border-radius: 0;
}
.works-collection-skeleton__body {
padding: 13px 14px 15px;
}
.works-collection-skeleton__body .van-skeleton__title {
width: 54%;
height: 15px;
margin-bottom: 12px;
}
.works-collection-skeleton__body .van-skeleton__row {
height: 12px;
margin-top: 9px;
}
.works-collection-skeleton__body .van-skeleton__row:last-child {
width: 46%;
}
.works-intro-popup,
.works-intro-page {
background-color: #f3f7fd;
}
.works-intro-page {
position: relative;
min-height: 100vh;
}
.works-intro-page .van-nav-bar {
background-color: #fff;
}
.works-intro-page .van-nav-bar__title {
color: #1f2937;
font-size: 16px;
}
.works-intro-page .van-nav-bar .van-icon,
.works-intro-page .van-nav-bar__text {
color: #397fbd;
}
.works-intro-scroll {
height: calc(100vh - 46px);
padding: 12px 12px calc(92px + env(safe-area-inset-bottom));
overflow-x: hidden;
overflow-y: auto;
box-sizing: border-box;
}
.works-intro-hero {
display: flex;
min-height: 82px;
padding: 16px;
align-items: center;
box-sizing: border-box;
border-radius: 12px;
color: #fff;
background: linear-gradient(120deg, #2d7eea 0%, #5ba6f7 100%);
box-shadow: 0 8px 20px rgba(45, 126, 234, .18);
}
.works-intro-hero__icon {
display: flex;
width: 44px;
height: 44px;
margin-right: 12px;
flex: 0 0 44px;
align-items: center;
justify-content: center;
border-radius: 11px;
color: #2d7eea;
background-color: rgba(255, 255, 255, .92);
font-size: 25px;
}
.works-intro-hero__title {
font-size: 17px;
line-height: 24px;
font-weight: 600;
}
.works-intro-hero__subtitle {
margin-top: 3px;
font-size: 12px;
line-height: 18px;
opacity: .9;
}
.works-intro-card {
margin-top: 12px;
padding: 18px 16px;
overflow: hidden;
border-radius: 12px;
background-color: #fff;
box-shadow: 0 7px 20px rgba(55, 85, 126, .08);
}
.works-intro-content,
.works-intro-content div,
.works-intro-content p,
.works-intro-content span,
.works-intro-content li {
color: #3f4c5e !important;
font-size: 14px !important;
line-height: 1.8 !important;
white-space: normal;
word-break: break-word;
}
.works-intro-content h1,
.works-intro-content h2,
.works-intro-content h3 {
margin: 16px 0 10px !important;
color: #1f2f43 !important;
font-size: 19px !important;
line-height: 1.5 !important;
font-weight: 600 !important;
}
.works-intro-content > :first-child {
margin-top: 0 !important;
}
.works-intro-content p {
margin-top: 0 !important;
margin-bottom: 8px !important;
}
.works-intro-content img {
max-width: 100% !important;
height: auto !important;
border-radius: 8px;
}
.works-intro-content table {
display: block;
max-width: 100%;
overflow-x: auto;
border-collapse: collapse;
}
.works-intro-empty {
display: flex;
min-height: 180px;
margin-top: 12px;
align-items: center;
justify-content: center;
flex-direction: column;
row-gap: 10px;
border-radius: 12px;
color: #9aa7b8;
background-color: #fff;
box-shadow: 0 7px 20px rgba(55, 85, 126, .08);
font-size: 14px;
}
.works-intro-empty .van-icon {
color: #83b5f4;
font-size: 36px;
}
.works-intro-footer {
position: fixed;
right: 0;
bottom: 0;
left: 0;
z-index: 5;
padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
border-top: 1px solid #edf1f6;
background-color: rgba(255, 255, 255, .96);
box-sizing: border-box;
}
.works-intro-footer .van-button {
height: 42px;
border: 0;
border-radius: 21px;
font-size: 15px;
background-color: #1989fa;
box-shadow: 0 6px 16px rgba(25, 137, 250, .22);
}
.works-intro-footer .van-button--disabled {
color: #8b98aa !important;
background-color: #eef2f7 !important;
opacity: 1;
box-shadow: none;
}
.works-intro-footer .van-count-down {
display: inline-block;
margin-left: 4px;
color: #fff;
font-size: 13px;
vertical-align: middle;
}
</style>
<div id="app">
<!-- 导航栏 -->
<van-nav-bar title="作品上传" left-text="返回" left-arrow placeholder
@click-left="historyBack" fixed></van-nav-bar>
<div id="app" v-cloak>
<div class="works-collection-page">
<van-nav-bar title="作品征集" left-text="返回" left-arrow @click-left="handlePageBack" fixed placeholder></van-nav-bar>
<van-sticky offset-top="46px">
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<year-van-dropdown-item :num="5" @change="doSearch" v-model="pageForm.year"></year-van-dropdown-item>
<van-dropdown-item v-model="pageForm.activityType" :options="typeOptions" :multiple="false"
@change="doSearch"></van-dropdown-item>
</van-dropdown-menu>
</van-sticky>
<div class="works-collection-content">
<section class="works-collection-hero">
<img class="works-collection-hero__image" src="/assets/mobile/img/activity/hero/brand-hero-v2.png" alt="作品征集插画">
<div class="works-collection-hero__copy">
<div class="works-collection-hero__title">作品征集</div>
<div class="works-collection-hero__subtitle">汇聚创意 · 展示风采</div>
</div>
</section>
<van-sticky :offset-top="46">
<div class="works-collection-sticky">
<van-search
v-model="pageForm.searchKeyword"
class="works-collection-search"
placeholder="搜索作品征集活动"
shape="round"
clearable
@search="doSearch"
@clear="doSearch"
></van-search>
<van-dropdown-menu class="works-collection-filter" :class="{'is-open': filterOpened}" active-color="#1989fa">
<year-van-dropdown-item :num="5" @change="doSearch" @open="onFilterOpen" @close="onFilterClose" v-model="pageForm.year"></year-van-dropdown-item>
<van-dropdown-item v-model="pageForm.activityType" :options="typeOptions" :multiple="false" @change="doSearch" @open="onFilterOpen" @close="onFilterClose"></van-dropdown-item>
</van-dropdown-menu>
</div>
</van-sticky>
<table-list api="/platform/activity/worksCollection/upload/h5/activityPageData"
:page_form.sync="pageForm"
ref="tableListRef"
title="name"
img="cover"
@ready="onReady"
>
<template v-slot="{index,row}">
<table-column label="面向对象">{{row.activityGroupName}}</table-column>
<table-column label="报名时间">
{{$moment(row.startDateTime).format('MM/DD HH:mm')
+ '~' + $moment(row.endDateTime).format('MM/DD HH:mm')}}
</table-column>
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<span>查看介绍</span>
<div v-if="skeletonLoading" class="works-collection-skeleton">
<div v-for="index in 3" :key="'upload-skeleton-' + index" class="works-collection-skeleton__card">
<van-skeleton class="works-collection-skeleton__cover" :row="1" animated></van-skeleton>
<div class="works-collection-skeleton__body">
<van-skeleton title :row="2" animated></van-skeleton>
</div>
</div>
</div>
<div class="action-btn" @click="onApply(row)">
<i class="fa fa-edit"></i>
<span>去报名</span>
</div>
</template>
</table-list>
<van-action-sheet v-model="infoVisible" title="详细信息">
<div class="info-container">
<van-image class="info-img" height="186" width="100%" :src="infoRow.cover"></van-image>
<div class="info-title">{{infoRow.name}}</div>
<pdf-preview style="height: calc(100vh - 46px - 44px - 186px - 57px)"
:content="infoRow.content"></pdf-preview>
<van-pull-refresh v-show="!skeletonLoading" v-model="refreshing" @refresh="onRefresh">
<van-list v-model="loading" :finished="finished" :immediate-check="false" @load="onLoad">
<div class="works-collection-list">
<div v-for="row in tableData" :key="row.id" class="works-collection-card" @click="onView(row)">
<img
v-if="hasCover(row) && !imageLoadErrors[row.id]"
class="works-collection-card__cover"
:src="row.cover"
:alt="row.name"
@error="handleImageError(row)"
>
<div v-else class="works-collection-card__cover-state" :class="{'is-error': hasCover(row)}">
<van-icon name="photo-o"></van-icon>
<span>{{ hasCover(row) ? '图片加载失败' : '暂无图片' }}</span>
</div>
<div class="works-collection-card__body">
<div class="works-collection-card__title">{{ row.name }}</div>
<div class="works-collection-card__time" v-if="row.startDateTime && row.endDateTime">
征集时间:{{ $moment(row.startDateTime).format('MM/DD HH:mm') }}~{{ $moment(row.endDateTime).format('MM/DD HH:mm') }}
</div>
<div class="works-collection-card__meta" v-if="row.activityGroupName">
<span class="works-collection-card__tag" v-if="row.activityGroupName">
<span class="works-collection-card__tag-label">面向对象</span>{{ row.activityGroupName }}
</span>
</div>
</div>
<van-icon class="works-collection-card__arrow" name="arrow"></van-icon>
</div>
</div>
</van-list>
<div v-if="!loading && tableData.length === 0" class="works-collection-empty">
<img src="/assets/mobile/img/activity/culture-empty.png" alt="作品征集空状态插画">
<div class="works-collection-empty__title">暂无作品征集活动</div>
<div class="works-collection-empty__hint">当前筛选条件下暂时没有可展示的活动</div>
</div>
</van-pull-refresh>
</div>
<div class="form-container">
<div class="form-actions">
</div>
<van-button @click="onApply(infoRow)" type="primary" block>
<span v-if="time >= 0">
去报名
</span>
<template v-else>
距离开始
<van-count-down :time="Math.abs(time)" class="count-down" format="DD 天 HH 时 mm 分 ss 秒"
@finish="time = 0"></van-count-down>
</template>
<van-popup class="works-intro-popup" :style="{height: '100%', width: '100%'}" position="right" safe-area-inset-bottom :close-on-click-overlay="false" v-model="infoVisible">
<div class="works-intro-page">
<van-sticky>
<van-nav-bar title="征集介绍" left-text="返回" left-arrow @click-left="closeInfo"></van-nav-bar>
</van-sticky>
<div class="works-intro-scroll">
<div class="works-intro-hero">
<div class="works-intro-hero__icon">
<van-icon name="description"></van-icon>
</div>
<div>
<div class="works-intro-hero__title">作品征集须知</div>
<div class="works-intro-hero__subtitle">请仔细阅读征集内容与作品要求</div>
</div>
</div>
<article v-if="infoRow.content" class="works-intro-card">
<div class="works-intro-content" v-html="infoRow.content"></div>
</article>
<div v-else class="works-intro-empty">
<van-icon name="description"></van-icon>
<span>暂无征集介绍</span>
</div>
</div>
<div class="works-intro-footer">
<van-button v-if="canApply(infoRow)" @click="onApply(infoRow)" type="primary" block round>上传作品</van-button>
<van-button v-else-if="time < 0" @click="onApply(infoRow)" type="primary" block round>
距离开始
<van-count-down :time="Math.abs(time)" format="DD 天 HH 时 mm 分 ss 秒" @finish="onCountdownFinish"></van-count-down>
</van-button>
<van-button v-else disabled block round>{{ registrationStatusText(infoRow) }}</van-button>
</div>
</div>
</van-action-sheet>
</van-popup>
<apply-form ref="applyFormRef"></apply-form>
</div>
<script nonce="${cspNonce!}">
<!--#include('applyForm.js'){}#-->
new Vue({
<!--#include("applyForm.js"){}#-->
const vue = new Vue({
el: "#app",
store,
components: {
"apply-form": applyForm
},
data() {
return {
time: 0,
tableData: [],
loading: false,
skeletonLoading: true,
finished: false,
refreshing: false,
imageLoadErrors: {},
filterOpened: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
@@ -114,53 +696,139 @@ layout("/layouts/platform_h5.html"){
typeOptions: [
{text: '全部', value: 1},
{text: '即将开始', value: 4},
{text: '报名中', value: 2},
{text: '征集中', value: 2},
{text: '已结束', value: 3},
],
infoVisible: false,
infoRow: {},
historyLayerPageKey: "works-collection-upload-h5",
historyLayerUnsubscribe: null
}
},
components: {
'apply-form': applyForm,
"pdf-preview": httpVueLoader("/components/plugins/sysFilePreview/PdfIndex.vue?v=" + new Date().getTime())
},
methods: {
onApply(row) {
if (this.$moment().isBefore(this.$moment(row.startDateTime))) {
this.onView(row)
hasCover(row) {
return !!(row && row.cover && String(row.cover).trim())
},
handleImageError(row) {
this.$set(this.imageLoadErrors, row.id, true)
},
onFilterOpen() {
this.$set(this, "filterOpened", true)
},
onFilterClose() {
this.$set(this, "filterOpened", false)
},
onCountdownFinish() {
this.$set(this, "time", 0)
},
isRegistrationClosed(row) {
return !!(row && row.endDateTime && !this.$moment().isBefore(this.$moment(row.endDateTime)))
},
canApply(row) {
if(!row || !row.startDateTime || !row.endDateTime) {
return false
}
const now = Date.now()
return now >= this.$moment(row.startDateTime).valueOf()
&& now <= this.$moment(row.endDateTime).valueOf()
},
registrationStatusText(row) {
if(!row || !row.startDateTime || !row.endDateTime) {
return '当前不可参与'
}
if(Date.now() < this.$moment(row.startDateTime).valueOf()) {
return '征集未开始'
}
return '征集已结束'
},
onView(row) {
this.$set(this, "infoRow", row)
this.$set(this, "time", this.$moment().diff(this.$moment(row.startDateTime), "milliseconds"))
window.h5HistoryLayerManager.open("works-collection-info")
},
closeInfo() {
window.h5HistoryLayerManager.close("works-collection-info")
},
handlePageBack() {
if (window.h5HistoryLayerManager.stack.length) {
window.h5HistoryLayerManager.close()
return
}
if (this.$moment().isAfter(this.$moment(row.endDateTime))) {
this.$toast.fail("活动已结束")
this.historyBack()
},
syncHistoryLayers(stack) {
this.$set(this, "infoVisible", stack.includes("works-collection-info"))
},
onApply(row) {
if(this.isRegistrationClosed(row)) {
this.$toast.fail("征集已结束")
return
}
if(this.$moment().isBefore(this.$moment(row.startDateTime))) {
this.onView(row)
return
}
this.$refs.applyFormRef.onOpen(row)
},
onView(row) {
this.infoRow = row
this.time = this.$moment().diff(this.$moment(row.startDateTime), 'milliseconds')
this.infoVisible = true
onLoad() {
if(!this.finished) {
this.loadActivities(false)
}
},
onReady() {
this.doSearch()
onRefresh() {
this.loadActivities(true)
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
loadActivities(reset) {
const showSkeleton = !!reset
if(reset) {
this.$set(this, "skeletonLoading", true)
this.$set(this.pageForm, "pageNumber", 1)
this.$set(this.pageForm, "totalCount", 0)
this.$set(this, "tableData", [])
this.$set(this, "finished", false)
this.$set(this, "imageLoadErrors", {})
}
this.$set(this, "loading", true)
this.$axios.post('/platform/activity/worksCollection/upload/h5/activityPageData', this.pageForm, {
h5SkeletonLoading: showSkeleton
}).then((res) => {
if(res.code === 0) {
const list = res.data && res.data.list ? res.data.list : []
this.$set(this, "tableData", this.pageForm.pageNumber === 1 ? list : this.tableData.concat(list))
this.$set(this.pageForm, "totalCount", res.data && res.data.totalCount ? res.data.totalCount : 0)
this.$set(this, "finished", this.tableData.length >= this.pageForm.totalCount)
if(!this.finished) {
this.$set(this.pageForm, "pageNumber", this.pageForm.pageNumber + 1)
}
}
}).finally(() => {
this.$set(this, "loading", false)
this.$set(this, "refreshing", false)
if(showSkeleton) this.$set(this, "skeletonLoading", false)
})
},
doSearch() {
this.loadActivities(true)
}
},
created() {
window.h5HistoryLayerManager.register(this.historyLayerPageKey, (stack) => {
this.syncHistoryLayers(stack)
})
this.$set(this, "historyLayerUnsubscribe", window.h5HistoryLayerManager.subscribe((stack) => {
this.syncHistoryLayers(stack)
}))
this.loadActivities(true)
},
beforeDestroy() {
if (this.historyLayerUnsubscribe) {
this.historyLayerUnsubscribe()
}
window.h5HistoryLayerManager.unregister(this.historyLayerPageKey)
}
})
</script>
<!--#
}
#-->
@@ -253,11 +253,24 @@ const PROPOSAL_INFO = {
this.$nextTick(() => {
this.$set(this, "reloadPending", false)
if (!this.visible || !this.currentBizId) return
this.resetScrollPosition()
this.getInfo()
this.getDoneTasks()
})
},
/**
* 重置提案详情的纵向滚动位置。
* 无参数;查找组件所在的 proposal-full-popup__scroll 容器并滚动到顶部,无匹配容器时安全跳过。
*/
resetScrollPosition() {
const componentRoot = this.$el
const scrollContainer = componentRoot && componentRoot.closest
? componentRoot.closest(".proposal-full-popup__scroll")
: null
if (scrollContainer) scrollContainer.scrollTop = 0
},
/**
* 获取字典字段的展示文本。
* options 为 PROPOSAL_SOURCE 等字典数组,value 为业务保存的字典 code;返回名称字符串,无匹配项时返回原值或 --。
@@ -46,7 +46,7 @@ layout("/layouts/platform_h5.html"){
.proposal-hero__title {
margin: 0;
color: var(--proposal-primary);
font-size: 23px;
font-size: 25px;
line-height: 30px;
font-weight: 700;
letter-spacing: 1px;
@@ -55,7 +55,7 @@ layout("/layouts/platform_h5.html"){
.proposal-hero__subtitle {
margin-top: 2px;
color: #718096;
font-size: 11px;
font-size: 13px;
line-height: 18px;
}
@@ -93,7 +93,7 @@ layout("/layouts/platform_h5.html"){
.proposal-notice__title {
color: #1989fa;
font-size: 12px;
font-size: 14px;
line-height: 18px;
font-weight: 600;
}
@@ -101,7 +101,7 @@ layout("/layouts/platform_h5.html"){
.proposal-notice__text {
margin-top: 2px;
color: #8a97a8;
font-size: 10px;
font-size: 12px;
line-height: 17px;
}
@@ -115,7 +115,7 @@ layout("/layouts/platform_h5.html"){
border: 0;
background: transparent;
color: var(--proposal-primary);
font-size: 10px;
font-size: 12px;
}
.proposal-card {
@@ -132,7 +132,7 @@ layout("/layouts/platform_h5.html"){
margin: 0;
padding: 12px 14px 8px 21px;
color: #354255;
font-size: 13px;
font-size: 15px;
line-height: 20px;
font-weight: 600;
}
@@ -168,14 +168,14 @@ layout("/layouts/platform_h5.html"){
.proposal-card .van-field__label {
width: 84px;
color: #596779;
font-size: 12px;
font-size: 14px;
line-height: 24px;
}
.proposal-card .van-field__value,
.proposal-card .van-field__control {
color: #4f5d70;
font-size: 12px;
font-size: 14px;
line-height: 24px;
}
@@ -186,7 +186,7 @@ layout("/layouts/platform_h5.html"){
.proposal-card .van-field__word-limit {
margin-top: 2px;
color: #b5bdc8;
font-size: 9px;
font-size: 11px;
line-height: 14px;
}
@@ -225,8 +225,8 @@ layout("/layouts/platform_h5.html"){
.proposal-actions .van-button {
height: 40px;
margin: 0;
border-radius: 6px;
font-size: 14px;
border-radius: 14px;
font-size: 16px;
}
.proposal-actions .van-button:first-child {
@@ -239,14 +239,13 @@ layout("/layouts/platform_h5.html"){
.proposal-actions .van-button:last-child {
flex: 1.08;
border-color: var(--proposal-primary);
border-radius: 14px;
background: var(--proposal-primary);
}
.proposal-actions .van-icon {
margin-right: 5px;
font-size: 15px;
vertical-align: -2px;
#app > .van-nav-bar .van-nav-bar__title,
#app > .van-nav-bar .van-nav-bar__text,
#app > .van-nav-bar .van-nav-bar__arrow {
font-size: 18px;
}
.suggestion-unit-picker {
@@ -267,7 +266,7 @@ layout("/layouts/platform_h5.html"){
.picker-cancel, .picker-confirm {
background: none;
border: none;
font-size: 16px;
font-size: 18px;
color: #1989fa;
cursor: pointer;
}
@@ -277,11 +276,21 @@ layout("/layouts/platform_h5.html"){
}
.picker-title {
font-size: 16px;
font-size: 18px;
font-weight: 500;
color: #323233;
}
.proposal-write-page .van-picker-column__item {
font-size: 18px;
}
.proposal-write-page .van-picker__cancel,
.proposal-write-page .van-picker__confirm,
.proposal-write-page .van-search__field {
font-size: 16px;
}
.search-container {
padding: 12px 16px;
background-color: #fff;
@@ -303,7 +312,7 @@ layout("/layouts/platform_h5.html"){
.option-text {
margin-left: 12px;
flex: 1;
font-size: 14px;
font-size: 16px;
color: #323233;
}
@@ -324,6 +333,7 @@ layout("/layouts/platform_h5.html"){
width: 100%;
padding: 0 16px; /* 左右留白 */
box-sizing: border-box;
font-size: 16px;
}
.notice-dialog-content p {
@@ -332,6 +342,16 @@ layout("/layouts/platform_h5.html"){
text-indent: 2em; /* 中文首行缩进更规范 */
}
#app .van-action-sheet__header,
.van-dialog__header {
font-size: 18px;
}
#app .van-action-sheet__cancel,
.van-dialog__message {
font-size: 16px;
}
@media (min-width: 600px) {
.proposal-hero__content,
.proposal-main {
@@ -385,7 +405,7 @@ layout("/layouts/platform_h5.html"){
<van-form ref="formRef">
<!-- 基本信息:保留当前教代会、代表身份及联系方式等业务字段。 -->
<section class="proposal-card">
<h2 class="proposal-card__title">一、基本信息</h2>
<h2 class="proposal-card__title">基本信息</h2>
<van-cell-group :border="false">
<van-field
v-model="formData.createUserName"
@@ -467,7 +487,7 @@ layout("/layouts/platform_h5.html"){
<!-- 提案内容:字段和校验继续以现有接口及实体定义为准。 -->
<section class="proposal-card">
<h2 class="proposal-card__title">二、提案内容</h2>
<h2 class="proposal-card__title">提案内容</h2>
<van-cell-group :border="false">
<van-field
v-model="formData.name"
@@ -540,7 +560,7 @@ layout("/layouts/platform_h5.html"){
<!-- 附件材料仅使用卡片分组,上传控件保持项目原有样式和结果结构。 -->
<section class="proposal-card">
<h2 class="proposal-card__title">三、附件材料</h2>
<h2 class="proposal-card__title">附件材料</h2>
<van-cell-group :border="false">
<van-field label="附件" name="files" class="direction-column-field">
<h5-file-upload
@@ -613,13 +633,13 @@ layout("/layouts/platform_h5.html"){
<!-- 保存、首次提交和重新提交继续共用原业务入口。 -->
<div class="proposal-actions" v-if="isWriteTime">
<van-button plain type="info" :loading="formLoading && loadingAction === 'save'" :disabled="formLoading" @click="onSave">
<van-icon name="records"></van-icon>保存草稿
保存草稿
</van-button>
<van-button type="primary" :loading="formLoading && loadingAction === 'submit'" :disabled="formLoading" @click="onSubmit" v-if="!taskId">
<van-icon name="guide-o"></van-icon>提交提案
提交提案
</van-button>
<van-button type="primary" :loading="formLoading && loadingAction === 'submitAgain'" :disabled="formLoading" @click="onSubmitAgain" v-else>
<van-icon name="guide-o"></van-icon>重新提交
重新提交
</van-button>
</div>
</div>
@@ -12,6 +12,12 @@ layout("/layouts/platform_h5.html"){
color: #1989fa;
}
#app > .van-nav-bar .van-nav-bar__title,
#app > .van-nav-bar .van-nav-bar__text,
#app > .van-nav-bar .van-nav-bar__arrow {
font-size: 18px;
}
.search-dialog {
height: 80%;
}
@@ -38,7 +44,7 @@ layout("/layouts/platform_h5.html"){
margin: 0;
padding: 12px 14px 8px 21px;
color: #354255;
font-size: 13px;
font-size: 15px;
line-height: 20px;
font-weight: 600;
background: transparent;
@@ -63,7 +69,7 @@ layout("/layouts/platform_h5.html"){
.condolence-card__desc {
padding: 0 14px 8px 21px;
color: orangered;
font-size: 12px;
font-size: 14px;
line-height: 18px;
font-weight: normal;
}
@@ -83,14 +89,14 @@ layout("/layouts/platform_h5.html"){
.condolence-apply-form .form-section .van-field__label {
width: 84px;
color: #596779;
font-size: 12px;
font-size: 14px;
line-height: 24px;
}
.condolence-apply-form .form-section .van-field__value,
.condolence-apply-form .form-section .van-field__control {
color: #4f5d70;
font-size: 12px;
font-size: 14px;
line-height: 24px;
}
@@ -120,8 +126,8 @@ layout("/layouts/platform_h5.html"){
.condolence-apply-actions .van-button {
height: 40px;
margin: 0;
border-radius: 6px;
font-size: 14px;
border-radius: 14px !important;
font-size: 16px;
}
.condolence-apply-actions .van-button:first-child {
@@ -134,9 +140,28 @@ layout("/layouts/platform_h5.html"){
.condolence-apply-actions .van-button:last-child {
flex: 1.08;
border-color: #1989fa;
border-radius: 14px;
background: #1989fa;
}
#app .van-action-sheet__header,
#app .van-picker__title,
.van-dialog__header {
font-size: 18px;
}
#app .van-action-sheet__name,
#app .van-action-sheet__cancel,
#app .van-empty__description,
#app .van-search__field,
#app .van-picker__cancel,
#app .van-picker__confirm,
.van-dialog__message {
font-size: 16px;
}
#app .van-picker-column__item {
font-size: 18px;
}
</style>
<div id="app">
@@ -23,6 +23,13 @@ layout("/layouts/platform_h5.html"){
.condolence-list-filter .van-dropdown-item__option { display: flex; min-height: 48px; padding: 0 16px; align-items: center; color: #46566c; font-size: 14px; }
.condolence-list-filter .van-dropdown-item__option .van-cell__title { display: flex; min-height: 20px; align-items: center; line-height: 20px; }
.condolence-list-content { padding: 0 12px; }
.condolence-list-skeleton { padding-top: 2px; }
.condolence-list-skeleton__card { margin-bottom: 12px; padding: 18px 14px 14px; border: 1px solid #e9eff7; border-radius: 12px; background: #fff; box-shadow: 0 5px 16px rgba(43, 73, 112, .08); }
.condolence-list-skeleton__card .van-skeleton { padding: 0; }
.condolence-list-skeleton__card .van-skeleton__title { width: 42%; height: 18px; margin-bottom: 18px; }
.condolence-list-skeleton__card .van-skeleton__row { height: 14px; margin-top: 12px; }
.condolence-list-skeleton__card .van-skeleton__row:nth-child(2n) { width: 72% !important; }
.condolence-list-skeleton__card .van-skeleton__row:last-child { width: 34% !important; margin-top: 18px; margin-left: auto; }
.condolence-list-page .table-list-container { margin-top: 0; padding-bottom: 2px; }
.condolence-list-page .table-list-container .table-list-item { margin-bottom: 12px; padding: 14px; border: 1px solid #e9eff7; border-radius: 12px; box-shadow: 0 5px 16px rgba(43, 73, 112, .08); }
.condolence-card-header { display: flex; margin-bottom: 8px; align-items: flex-start; justify-content: space-between; }
@@ -72,7 +79,14 @@ layout("/layouts/platform_h5.html"){
</van-sticky>
<main class="condolence-list-content">
<table-list api="/platform/condolence/branchUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" @ready="onReady">
<div class="condolence-list-skeleton" v-if="skeletonLoading">
<div class="condolence-list-skeleton__card" v-for="index in 3" :key="'branch-approval-skeleton-' + index">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<table-list api="/platform/condolence/branchUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef"
v-show="!skeletonLoading" :show_loading_overlay="false" @ready="onReady"
@loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="condolence-card-header">
<div class="condolence-card-heading">
@@ -180,6 +194,7 @@ layout("/layouts/platform_h5.html"){
{text: "未审核", value: "0"},
{text: "已审核", value: "1"}
],
skeletonLoading: true,
formData: {},
showApprovalForm: false,
formLoading: false,
@@ -191,6 +206,10 @@ layout("/layouts/platform_h5.html"){
}
},
methods: {
// state.initial 表示当前查询前列表为空,仅此阶段使用整组卡片骨架屏。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, "skeletonLoading", state.loading)
},
// 审核状态下拉值为字符串,接口参数 approval 继续使用布尔值。
onApprovalFilterChange(val) {
this.$set(this.pageForm, "approval", val === "1")
@@ -284,7 +303,7 @@ layout("/layouts/platform_h5.html"){
})
},
queryCondolenceType() {
this.$axios.post('/platform/condolence/type/queryCondolenceType')
this.$axios.post('/platform/condolence/type/queryCondolenceType', {}, {h5SkeletonLoading: true})
.then((res) => {
const typeList = res.data || []
this.$set(this, "typeOptions", [
@@ -127,6 +127,44 @@ layout("/layouts/platform_h5.html"){
padding: 0 12px;
}
.condolence-list-skeleton {
padding-top: 2px;
}
.condolence-list-skeleton__card {
margin-bottom: 12px;
padding: 18px 14px 14px;
border: 1px solid #e9eff7;
border-radius: 12px;
background: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
}
.condolence-list-skeleton__card .van-skeleton {
padding: 0;
}
.condolence-list-skeleton__card .van-skeleton__title {
width: 42%;
height: 18px;
margin-bottom: 18px;
}
.condolence-list-skeleton__card .van-skeleton__row {
height: 14px;
margin-top: 12px;
}
.condolence-list-skeleton__card .van-skeleton__row:nth-child(2n) {
width: 72% !important;
}
.condolence-list-skeleton__card .van-skeleton__row:last-child {
width: 34% !important;
margin-top: 18px;
margin-left: auto;
}
.condolence-list-page .table-list-container {
margin-top: 0;
padding-bottom: 2px;
@@ -278,7 +316,14 @@ layout("/layouts/platform_h5.html"){
</van-sticky>
<main class="condolence-list-content">
<table-list api="/platform/condolence/mine/pageData" :page_form.sync="pageForm" ref="tableListRef" @ready="onReady">
<div class="condolence-list-skeleton" v-if="skeletonLoading">
<div class="condolence-list-skeleton__card" v-for="index in 3" :key="'mine-skeleton-' + index">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<table-list api="/platform/condolence/mine/pageData" :page_form.sync="pageForm" ref="tableListRef"
v-show="!skeletonLoading" :show_loading_overlay="false" @ready="onReady"
@loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="condolence-card-header">
<div class="condolence-card-heading">
@@ -351,21 +396,28 @@ layout("/layouts/platform_h5.html"){
type: null
},
typeOptions: [],
skeletonLoading: true,
}
},
methods: {
async onReady() {
const typeList = await this.queryCondolenceType()
this.typeOptions = [
{
text: "全部慰问类型",
value: null
}
].concat(typeList.map((v) => ({ text: v.name, value: v.id })))
if (this.typeOptions.length > 0) {
this.pageForm.type = this.typeOptions[0].value
this.doSearch()
}
// state.initial 表示当前查询前列表为空,仅此阶段使用整组卡片骨架屏。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, "skeletonLoading", state.loading)
},
onReady() {
this.queryCondolenceType()
.then((typeList) => {
this.$set(this, "typeOptions", [
{
text: "全部慰问类型",
value: null
}
].concat(typeList.map((v) => ({ text: v.name, value: v.id }))))
if (this.typeOptions.length > 0) {
this.$set(this.pageForm, "type", this.typeOptions[0].value)
this.doSearch()
}
})
},
onRevoke(row) {
this.$dialog.confirm({
@@ -403,9 +455,9 @@ layout("/layouts/platform_h5.html"){
})
.catch(() => {})
},
async queryCondolenceType() {
const res = await this.$axios.post('/platform/condolence/type/queryCondolenceType')
return res.data
queryCondolenceType() {
return this.$axios.post('/platform/condolence/type/queryCondolenceType', {}, {h5SkeletonLoading: true})
.then((res) => res.data || [])
},
doSearch() {
this.$nextTick(() => {
@@ -23,6 +23,13 @@ layout("/layouts/platform_h5.html"){
.condolence-list-filter .van-dropdown-item__option { display: flex; min-height: 48px; padding: 0 16px; align-items: center; color: #46566c; font-size: 14px; }
.condolence-list-filter .van-dropdown-item__option .van-cell__title { display: flex; min-height: 20px; align-items: center; line-height: 20px; }
.condolence-list-content { padding: 0 12px; }
.condolence-list-skeleton { padding-top: 2px; }
.condolence-list-skeleton__card { margin-bottom: 12px; padding: 18px 14px 14px; border: 1px solid #e9eff7; border-radius: 12px; background: #fff; box-shadow: 0 5px 16px rgba(43, 73, 112, .08); }
.condolence-list-skeleton__card .van-skeleton { padding: 0; }
.condolence-list-skeleton__card .van-skeleton__title { width: 42%; height: 18px; margin-bottom: 18px; }
.condolence-list-skeleton__card .van-skeleton__row { height: 14px; margin-top: 12px; }
.condolence-list-skeleton__card .van-skeleton__row:nth-child(2n) { width: 72% !important; }
.condolence-list-skeleton__card .van-skeleton__row:last-child { width: 34% !important; margin-top: 18px; margin-left: auto; }
.condolence-list-page .table-list-container { margin-top: 0; padding-bottom: 2px; }
.condolence-list-page .table-list-container .table-list-item { margin-bottom: 12px; padding: 14px; border: 1px solid #e9eff7; border-radius: 12px; box-shadow: 0 5px 16px rgba(43, 73, 112, .08); }
.condolence-card-header { display: flex; margin-bottom: 8px; align-items: flex-start; justify-content: space-between; }
@@ -72,7 +79,14 @@ layout("/layouts/platform_h5.html"){
</van-sticky>
<main class="condolence-list-content">
<table-list api="/platform/condolence/schoolPrincipalApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" @ready="onReady">
<div class="condolence-list-skeleton" v-if="skeletonLoading">
<div class="condolence-list-skeleton__card" v-for="index in 3" :key="'school-principal-approval-skeleton-' + index">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<table-list api="/platform/condolence/schoolPrincipalApproval/pageData" :page_form.sync="pageForm" ref="tableListRef"
v-show="!skeletonLoading" :show_loading_overlay="false" @ready="onReady"
@loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="condolence-card-header">
<div class="condolence-card-heading">
@@ -180,6 +194,7 @@ layout("/layouts/platform_h5.html"){
{text: "未审核", value: "0"},
{text: "已审核", value: "1"}
],
skeletonLoading: true,
formData: {},
showApprovalForm: false,
formLoading: false,
@@ -191,6 +206,10 @@ layout("/layouts/platform_h5.html"){
}
},
methods: {
// state.initial 表示当前查询前列表为空,仅此阶段使用整组卡片骨架屏。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, "skeletonLoading", state.loading)
},
// 审核状态下拉值为字符串,接口参数 approval 继续使用布尔值。
onApprovalFilterChange(val) {
this.$set(this.pageForm, "approval", val === "1")
@@ -284,7 +303,7 @@ layout("/layouts/platform_h5.html"){
})
},
queryCondolenceType() {
this.$axios.post('/platform/condolence/type/queryCondolenceType')
this.$axios.post('/platform/condolence/type/queryCondolenceType', {}, {h5SkeletonLoading: true})
.then((res) => {
const typeList = res.data || []
this.$set(this, "typeOptions", [
@@ -23,6 +23,13 @@ layout("/layouts/platform_h5.html"){
.condolence-list-filter .van-dropdown-item__option { display: flex; min-height: 48px; padding: 0 16px; align-items: center; color: #46566c; font-size: 14px; }
.condolence-list-filter .van-dropdown-item__option .van-cell__title { display: flex; min-height: 20px; align-items: center; line-height: 20px; }
.condolence-list-content { padding: 0 12px; }
.condolence-list-skeleton { padding-top: 2px; }
.condolence-list-skeleton__card { margin-bottom: 12px; padding: 18px 14px 14px; border: 1px solid #e9eff7; border-radius: 12px; background: #fff; box-shadow: 0 5px 16px rgba(43, 73, 112, .08); }
.condolence-list-skeleton__card .van-skeleton { padding: 0; }
.condolence-list-skeleton__card .van-skeleton__title { width: 42%; height: 18px; margin-bottom: 18px; }
.condolence-list-skeleton__card .van-skeleton__row { height: 14px; margin-top: 12px; }
.condolence-list-skeleton__card .van-skeleton__row:nth-child(2n) { width: 72% !important; }
.condolence-list-skeleton__card .van-skeleton__row:last-child { width: 34% !important; margin-top: 18px; margin-left: auto; }
.condolence-list-page .table-list-container { margin-top: 0; padding-bottom: 2px; }
.condolence-list-page .table-list-container .table-list-item { margin-bottom: 12px; padding: 14px; border: 1px solid #e9eff7; border-radius: 12px; box-shadow: 0 5px 16px rgba(43, 73, 112, .08); }
.condolence-card-header { display: flex; margin-bottom: 8px; align-items: flex-start; justify-content: space-between; }
@@ -72,7 +79,14 @@ layout("/layouts/platform_h5.html"){
</van-sticky>
<main class="condolence-list-content">
<table-list api="/platform/condolence/schoolUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" @ready="onReady">
<div class="condolence-list-skeleton" v-if="skeletonLoading">
<div class="condolence-list-skeleton__card" v-for="index in 3" :key="'school-union-approval-skeleton-' + index">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<table-list api="/platform/condolence/schoolUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef"
v-show="!skeletonLoading" :show_loading_overlay="false" @ready="onReady"
@loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="condolence-card-header">
<div class="condolence-card-heading">
@@ -180,6 +194,7 @@ layout("/layouts/platform_h5.html"){
{text: "未审核", value: "0"},
{text: "已审核", value: "1"}
],
skeletonLoading: true,
formData: {},
showApprovalForm: false,
formLoading: false,
@@ -191,6 +206,10 @@ layout("/layouts/platform_h5.html"){
}
},
methods: {
// state.initial 表示当前查询前列表为空,仅此阶段使用整组卡片骨架屏。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, "skeletonLoading", state.loading)
},
// 审核状态下拉值为字符串,接口参数 approval 继续使用布尔值。
onApprovalFilterChange(val) {
this.$set(this.pageForm, "approval", val === "1")
@@ -284,7 +303,7 @@ layout("/layouts/platform_h5.html"){
})
},
queryCondolenceType() {
this.$axios.post('/platform/condolence/type/queryCondolenceType')
this.$axios.post('/platform/condolence/type/queryCondolenceType', {}, {h5SkeletonLoading: true})
.then((res) => {
const typeList = res.data || []
this.$set(this, "typeOptions", [
@@ -1,17 +1,305 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style>
#app {
--color-primary: #1989fa;
}
#app > .van-nav-bar .van-icon,
#app > .van-nav-bar .van-nav-bar__text {
color: #1989fa;
}
#app > .van-nav-bar .van-nav-bar__title,
#app > .van-nav-bar .van-nav-bar__text,
#app > .van-nav-bar .van-nav-bar__arrow {
font-size: 18px;
}
.difficult-help-apply-form.form-container {
min-height: 100%;
padding: 12px 15px calc(84px + env(safe-area-inset-bottom));
box-sizing: border-box;
background: #f3f7fc;
}
.difficult-help-apply-form .form-section {
margin-bottom: 11px;
overflow: hidden;
border: 1px solid #e9eff7;
border-radius: 10px;
background: #fff;
box-shadow: 0 3px 11px rgba(51, 87, 126, 0.08);
}
.difficult-help-apply-form .difficult-help-card__title {
position: relative;
margin: 0;
padding: 12px 14px 8px 21px;
color: #354255;
font-size: 15px;
line-height: 20px;
font-weight: 600;
}
.difficult-help-apply-form .difficult-help-card__title::before {
position: absolute;
top: 15px;
bottom: 11px;
left: 12px;
width: 2px;
border-radius: 2px;
background: #1989fa;
content: "";
}
.difficult-help-apply-form .form-section .van-cell-group {
margin: 0;
background: transparent;
}
.difficult-help-apply-form .form-section .van-cell {
min-height: 44px;
padding: 10px 14px;
background: transparent;
}
.difficult-help-apply-form .form-section .van-cell:not(:last-child)::after {
right: 14px;
left: 14px;
border-color: #eef2f7;
}
.difficult-help-apply-form .form-section .van-field__label {
width: 84px;
color: #596779;
font-size: 14px;
line-height: 24px;
}
.difficult-help-apply-form .form-section .van-field__value,
.difficult-help-apply-form .form-section .van-field__control {
color: #4f5d70;
font-size: 14px;
line-height: 24px;
}
.difficult-help-apply-form .form-section .van-field__control::placeholder {
color: #b1bac6;
}
.difficult-help-apply-form .form-section .van-cell--required::before {
left: 6px;
color: #f06464;
}
.difficult-help-apply-actions {
position: fixed;
z-index: 100;
right: 0;
bottom: 0;
left: 0;
display: flex;
gap: 9px;
padding: 10px 18px calc(10px + env(safe-area-inset-bottom));
border-top: 1px solid #edf1f6;
background: rgba(255, 255, 255, 0.98);
box-shadow: 0 -4px 14px rgba(50, 78, 112, 0.06);
}
.difficult-help-apply-actions .van-button {
height: 40px;
margin: 0;
border-radius: 14px !important;
font-size: 16px;
}
.difficult-help-apply-actions .van-button:first-child {
flex: 1;
color: #1989fa;
border-color: #1989fa;
background: #fff;
}
.difficult-help-apply-actions .van-button:last-child {
flex: 1.08;
border-color: #1989fa;
background: #1989fa;
}
#app .van-action-sheet__header,
#app .van-picker__title,
.van-dialog__header {
font-size: 18px;
}
#app .van-action-sheet__name,
#app .van-action-sheet__cancel,
#app .van-empty__description,
#app .van-search__field,
#app .van-picker__cancel,
#app .van-picker__confirm,
.van-dialog__message {
font-size: 16px;
}
#app .van-picker-column__item {
font-size: 18px;
}
.difficult-help-option-popup {
display: flex;
height: 50%;
padding-bottom: env(safe-area-inset-bottom);
flex-direction: column;
box-sizing: border-box;
background: #f5f7fa;
}
.difficult-help-option-popup__header {
display: flex;
min-height: 52px;
padding: 0 16px;
align-items: center;
justify-content: space-between;
flex: none;
border-bottom: 1px solid #edf1f6;
background: #fff;
}
.difficult-help-option-popup__title {
color: #263548;
font-size: 17px;
line-height: 24px;
font-weight: 600;
}
.difficult-help-option-popup__action {
min-width: 48px;
padding: 8px 0;
border: 0;
color: #1989fa;
font-size: 15px;
line-height: 22px;
text-align: left;
background: transparent;
}
.difficult-help-option-popup__action:last-child {
text-align: right;
}
.difficult-help-option-popup__action:disabled {
color: #b7c0cc;
}
.difficult-help-option-popup__list {
min-height: 0;
padding: 12px;
overflow-y: auto;
flex: 1;
-webkit-overflow-scrolling: touch;
}
.difficult-help-option-popup__search {
padding: 10px 12px 0;
flex: none;
background: #f5f7fa;
}
.difficult-help-option-popup__search .van-search__content {
border: 1px solid #e3e9f1;
background: #fff;
}
.difficult-help-option-popup__empty {
padding: 44px 18px;
color: #98a4b3;
font-size: 14px;
line-height: 22px;
text-align: center;
}
.difficult-help-option-popup__option {
display: flex;
width: 100%;
min-height: 58px;
margin-bottom: 10px;
padding: 13px 14px;
align-items: center;
justify-content: space-between;
gap: 12px;
box-sizing: border-box;
border: 1px solid #e4eaf2;
border-radius: 10px;
color: #46566c;
text-align: left;
background: #fff;
}
.difficult-help-option-popup__option:last-child {
margin-bottom: 0;
}
.difficult-help-option-popup__option.is-selected {
border-color: #1989fa;
color: #176fbd;
background: #eef7ff;
box-shadow: 0 3px 10px rgba(25, 137, 250, 0.1);
}
.difficult-help-option-popup__option-text {
min-width: 0;
flex: 1;
font-size: 15px;
line-height: 23px;
white-space: normal;
word-break: break-word;
overflow-wrap: anywhere;
}
.difficult-help-option-popup__recipient {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
}
.difficult-help-option-popup__recipient-name {
color: #304157;
font-size: 16px;
line-height: 23px;
font-weight: 600;
}
.difficult-help-option-popup__recipient-unit {
margin-top: 5px;
color: #8290a3;
font-size: 13px;
line-height: 20px;
white-space: normal;
word-break: break-word;
}
.difficult-help-option-popup__option.is-selected .difficult-help-option-popup__recipient-name,
.difficult-help-option-popup__option.is-selected .difficult-help-option-popup__recipient-unit {
color: #176fbd;
}
</style>
<div id="app">
<div v-if="!isShow">
<van-empty image="error" :description="'抱歉,当前时间不能申请,可申请时间为' + time + '。'"></van-empty>
</div>
<div v-else>
<van-nav-bar title="帮扶申请" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-nav-bar title="帮扶申请" left-text="返回" left-arrow @click-left="handlePageBack" placeholder fixed></van-nav-bar>
<div class="form-container">
<van-form ref="formRef">
<van-cell-group title="申请人信息" class="form-section">
<van-form ref="formRef" class="form-container difficult-help-apply-form">
<section class="form-section">
<h2 class="difficult-help-card__title">申请人信息</h2>
<van-cell-group :border="false">
<van-field label="申请次数" v-model="formData.applyCount" readonly
required name="applyCount"></van-field>
<van-field label="填写人姓名" :rules="[{ required: true }]" v-model="formData.proxyUserName" readonly
@@ -29,28 +317,25 @@ layout("/layouts/platform_h5.html"){
</van-radio-group>
</template>
</van-field>
</van-cell-group>
</van-cell-group>
</section>
<van-cell-group title="受助人信息" class="form-section">
<section class="form-section">
<h2 class="difficult-help-card__title">受助人信息</h2>
<van-cell-group :border="false">
<template v-if="formData.mode == 2">
<van-field label="受助人" :rules="[{ required: true }]" v-model="formData.userName"
required name="userName" placeholder="请输入受补助人姓名后点击查询">
<template #button>
<van-button :disabled="formData.id" @click="queryRecipients(formData.userName)"
size="small" type="primary">查询
</van-button>
</template>
<van-field
label="受助人"
:rules="[{ required: true }]"
v-model="formData.userName"
:is-link="!formData.id"
readonly
required
name="userName"
placeholder="请选择受助人"
@click="openRecipientPopup">
</van-field>
<van-action-sheet
:actions="subsidizedList"
:close-on-click-overlay="false"
@cancel="userCancel"
@select="onUserSelect"
cancel-text="重新输入姓名或工号查询"
v-model="userSheetShow">
</van-action-sheet>
</template>
<template v-else>
<van-field label="受助人" :rules="[{ required: true }]" v-model="formData.userName" readonly
@@ -66,16 +351,8 @@ layout("/layouts/platform_h5.html"){
readonly
name="sex"
:rules="[{ required: true }]"
@click="showSexPicker = true"
@click="openOptionPopup('sex')"
></van-field>
<van-popup v-model="showSexPicker" position="bottom">
<van-picker
show-toolbar
:columns="['男', '女']"
@confirm="onSexConfirm"
@cancel="showSexPicker=false"
></van-picker>
</van-popup>
<van-field
v-model="formData.unionName"
@@ -171,47 +448,34 @@ layout("/layouts/platform_h5.html"){
:rules="[{ required: true }]"
></van-field>
</van-cell-group>
</van-cell-group>
</section>
<van-cell-group title="困难类型及材料" class="form-section">
<section class="form-section">
<h2 class="difficult-help-card__title">困难类型及材料</h2>
<van-cell-group :border="false">
<van-field
v-model="formData.subsidyName"
label="补助类型"
placeholder="请选择补助类型"
is-link
@click="showSubsidyPicker = true"
readonly
@click="openOptionPopup('subsidyType')"
required
name="subsidyName"
:rules="[{ required: true, message: '请选择补助类型' }]"
></van-field>
<van-popup v-model="showSubsidyPicker" position="bottom">
<van-picker
show-toolbar
:columns="subsidyTypeColumns"
@confirm="onSubsidyTypeConfirm"
@cancel="showSubsidyPicker=false">
</van-picker>
</van-popup>
<van-field
v-model="formData.subsidyStandardsName"
label="困难类型"
placeholder="请选择困难类型"
is-link
@click="showSubsidyStandardPicker = true"
readonly
@click="openOptionPopup('subsidyStandard')"
required
name="subsidyStandardsName"
:rules="[{ required: true, message: '请选择困难类型' }]"
></van-field>
<van-popup v-model="showSubsidyStandardPicker" position="bottom">
<van-picker
show-toolbar
:columns="subsidyStandardColumns"
@confirm="onSubsidyStandardConfirm"
@cancel="showSubsidyStandardPicker=false">
</van-picker>
</van-popup>
<van-field
v-model="formData.bankCardNum"
label="收款账户"
@@ -242,9 +506,12 @@ layout("/layouts/platform_h5.html"){
name="reason"
:rules="[{ required: true }]"
></van-field>
</van-cell-group>
</van-cell-group>
</section>
<van-cell-group title="家庭主要成员信息" class="form-section up_down">
<section class="form-section up_down">
<h2 class="difficult-help-card__title">家庭主要成员信息</h2>
<van-cell-group :border="false">
<div class="van-cell">
<div style="display: flex;justify-content: space-between">
<div style="display: flex;">
@@ -290,9 +557,12 @@ layout("/layouts/platform_h5.html"){
</div>
</template>
</div>
</van-cell-group>
</van-cell-group>
</section>
<van-cell-group title="附件信息" class="form-section">
<section class="form-section">
<h2 class="difficult-help-card__title">附件信息</h2>
<van-cell-group :border="false">
<van-field class="more-text" name="files" label="">
<template #input>
<h5-file-upload
@@ -306,9 +576,12 @@ layout("/layouts/platform_h5.html"){
></h5-file-upload>
</template>
</van-field>
</van-cell-group>
</van-cell-group>
</section>
<van-cell-group title="签字" class="form-section">
<section class="form-section">
<h2 class="difficult-help-card__title">签字确认</h2>
<van-cell-group :border="false">
<van-field class="more-text" name="sign" label="">
<template #input>
<h5-signature v-model="formData.sign" slot="input"></h5-signature>
@@ -317,15 +590,67 @@ layout("/layouts/platform_h5.html"){
<div style="color: red; padding: 10px;">
本人承诺:本人及家庭成员未有购买价格超过15万元的机动车,也未有两套及以上商品房,特此承诺!
</div>
</van-cell-group>
</van-cell-group>
</section>
<div class="form-actions">
<van-button native-type="button" plain type="info" @click="onSave">保存</van-button>
<van-button type="primary" @click="onSubmit" v-if="!taskId">提交</van-button>
<van-button type="primary" @click="onFinishTask" v-else>提交</van-button>
<div class="form-actions difficult-help-apply-actions">
<van-button native-type="button" plain round type="info" @click="onSave">保存申请</van-button>
<van-button round type="info" @click="onSubmit" v-if="!taskId">提交申请</van-button>
<van-button round type="info" @click="onFinishTask" v-else>提交申请</van-button>
</div>
</van-form>
</div>
<van-popup
v-model="showOptionPicker"
position="bottom"
round
:close-on-click-overlay="false"
class="difficult-help-option-popup">
<div class="difficult-help-option-popup__header">
<button type="button" class="difficult-help-option-popup__action"
@click="cancelOptionPopup">取消</button>
<div class="difficult-help-option-popup__title">{{optionPopupTitle}}</div>
<button type="button" class="difficult-help-option-popup__action"
:disabled="pendingOptionValue === null || pendingOptionValue === undefined || pendingOptionValue === ''"
@click="confirmOptionPopup">确认</button>
</div>
<van-search
v-if="optionPopupType === 'recipient'"
v-model.trim="recipientSearchKey"
class="difficult-help-option-popup__search"
shape="round"
clearable
placeholder="请输入姓名或工号搜索"
@search="queryRecipients"
@clear="clearRecipientSearchResults">
</van-search>
<div class="difficult-help-option-popup__list">
<div
v-if="optionPopupType === 'recipient' && !recipientHasSearched && optionPopupOptions.length === 0"
class="difficult-help-option-popup__empty">
输入姓名或工号后,点击键盘搜索键查询
</div>
<div
v-else-if="optionPopupType === 'recipient' && recipientHasSearched && optionPopupOptions.length === 0"
class="difficult-help-option-popup__empty">
未查询到符合条件的受助人
</div>
<button
v-for="option in optionPopupOptions"
:key="option.value"
type="button"
class="difficult-help-option-popup__option"
:class="{'is-selected': pendingOptionValue === option.value}"
:aria-pressed="pendingOptionValue === option.value"
@click="selectOptionPopupItem(option)">
<span v-if="optionPopupType === 'recipient'" class="difficult-help-option-popup__recipient">
<span class="difficult-help-option-popup__recipient-name">{{option.text}}</span>
<span class="difficult-help-option-popup__recipient-unit">{{option.description || '暂无单位信息'}}</span>
</span>
<span v-else class="difficult-help-option-popup__option-text">{{option.text}}</span>
</button>
</div>
</van-popup>
</div>
</div>
@@ -333,11 +658,11 @@ layout("/layouts/platform_h5.html"){
.mt5 {
margin-top: 5px;
}
.up_down > .van-cell {
.up_down > .van-cell-group > .van-cell {
flex-flow: column;
}
.up_down > .van-cell > .van-field__label {
.up_down > .van-cell-group > .van-cell > .van-field__label {
width: 100%;
}
</style>
@@ -350,10 +675,15 @@ layout("/layouts/platform_h5.html"){
return {
bizId: GetQueryString("bizId"),
taskId: GetQueryString("taskId"),
showSexPicker: false,
showDatePicker: false,
showSubsidyPicker: false,
showSubsidyStandardPicker: false,
showOptionPicker: false,
optionPopupType: "",
optionPopupTitle: "",
optionPopupOptions: [],
pendingOptionValue: "",
recipientSearchKey: "",
recipientHasSearched: false,
historyLayerPageKey: "difficult-help-apply",
formData: {
id: GetQueryString("bizId"),
mode: "1",
@@ -365,7 +695,6 @@ layout("/layouts/platform_h5.html"){
subsidyValue: "",
subsidyStandardsValue: "",
userSheetShow: false,
subsidizedList: [],
maxDate: new Date(),
@@ -380,19 +709,153 @@ layout("/layouts/platform_h5.html"){
},
methods: {
historyBack,
// 替他人申请时由只读字段主动打开受助人选择,已有业务数据时保持原禁止更换规则。
openRecipientPopup() {
if (this.formData.mode !== "2" || this.formData.id) return
this.$set(this, "recipientSearchKey", "")
this.$set(this, "recipientHasSearched", false)
this.$set(this, "subsidizedList", [])
this.openOptionPopup("recipient")
},
/**
* 根据选择类型配置并打开统一弹框。
* optionType 支持 recipient、sex、subsidyType、subsidyStandard;方法无返回值。
*/
openOptionPopup(optionType) {
let title = ""
let options = []
let currentValue = ""
if (optionType === "recipient") {
title = "选择受助人"
options = this.subsidizedList.map((item) => {
return {value: item.id, text: item.username, description: item.unitName, source: item}
})
const matchedRecipient = options.find((item) => {
return item.value === this.formData.userId
})
currentValue = matchedRecipient ? matchedRecipient.value : ""
} else if (optionType === "sex") {
title = "选择性别"
options = [{value: "男", text: "男"}, {value: "女", text: "女"}]
currentValue = this.formData.sex || ""
} else if (optionType === "subsidyType") {
title = "选择补助类型"
options = this.subsidyTypeColumns.slice()
const matchedSubsidy = options.find((item) => {
return item.text === this.formData.subsidyName
})
currentValue = matchedSubsidy
? matchedSubsidy.value
: (this.formData.subsidy || this.subsidyValue || "")
} else if (optionType === "subsidyStandard") {
title = "选择困难类型"
options = this.subsidyStandardColumns.slice()
const matchedStandard = options.find((item) => {
return item.text === this.formData.subsidyStandardsName
})
currentValue = matchedStandard
? matchedStandard.value
: (this.formData.subsidyStandards || this.subsidyStandardsValue || "")
}
this.$set(this, "optionPopupType", optionType)
this.$set(this, "optionPopupTitle", title)
this.$set(this, "optionPopupOptions", options)
this.$set(this, "pendingOptionValue", currentValue)
this.openPopup("showOptionPicker")
},
selectOptionPopupItem(option) {
this.$set(this, "pendingOptionValue", option.value)
},
cancelOptionPopup() {
if (this.optionPopupType === "recipient") {
this.$set(this, "recipientSearchKey", "")
this.$set(this, "recipientHasSearched", false)
this.$set(this, "subsidizedList", [])
}
this.closePopup("showOptionPicker")
},
clearRecipientSearchResults() {
this.$set(this, "recipientHasSearched", false)
this.$set(this, "subsidizedList", [])
this.$set(this, "optionPopupOptions", [])
this.$set(this, "pendingOptionValue", "")
},
/**
* 确认统一弹框选项并按类型回填表单。
* 选项 value 为业务值、text 为展示文本,recipient 额外携带 source 用户对象;方法无返回值。
*/
confirmOptionPopup() {
const selectedOption = this.optionPopupOptions.find((item) => {
return item.value === this.pendingOptionValue
})
if (!selectedOption) {
this.$toast("请选择" + this.optionPopupTitle.replace("选择", ""))
return
}
if (this.optionPopupType === "recipient") {
this.applySelectedUser(selectedOption.source)
this.$set(this, "recipientSearchKey", "")
this.$set(this, "recipientHasSearched", false)
this.$set(this, "subsidizedList", [])
} else if (this.optionPopupType === "sex") {
this.$set(this.formData, "sex", selectedOption.value)
} else if (this.optionPopupType === "subsidyType") {
this.$set(this.formData, "subsidyName", selectedOption.text)
this.$set(this, "subsidyValue", selectedOption.value)
} else if (this.optionPopupType === "subsidyStandard") {
this.$set(this.formData, "subsidyStandardsName", selectedOption.text)
this.$set(this, "subsidyStandardsValue", selectedOption.value)
}
this.closePopup("showOptionPicker")
},
/**
* 打开底部弹框并增加一层浏览器历史。
* popupKey 传弹框对应的 data 字段名;方法无返回值。
*/
openPopup(popupKey) {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open(popupKey)) return
this.syncPopupState(popupKey, true)
},
/**
* 主动关闭底部弹框并回退对应的浏览器历史。
* popupKey 传弹框对应的 data 字段名;返回键回调只同步界面状态,避免重复回退历史。
*/
closePopup(popupKey) {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.close(popupKey)) return
this.syncPopupState(popupKey, false)
},
syncPopupState(popupKey, visible) {
const popupKeys = ["showOptionPicker"]
if (popupKeys.includes(popupKey)) {
this.$set(this, popupKey, visible)
}
},
// 根据统一弹层历史栈同步界面,返回操作只关闭栈顶弹框,不重新加载当前页面。
syncHistoryLayerStack(stack) {
const popupKeys = ["showOptionPicker"]
popupKeys.forEach((popupKey) => {
this.syncPopupState(popupKey, stack.includes(popupKey))
})
},
// NavBar 返回与系统返回保持一致:有弹框先关闭弹框,无弹框再返回业务页面。
handlePageBack() {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.stack.length) {
window.h5HistoryLayerManager.close()
return
}
this.historyBack()
},
// 业务跳转前清空当前页面的弹框历史,避免返回时重新出现已关闭弹框。
clearPopupHistoryAndNavigate(url) {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.closeAllAndNavigate(url)) return
this.$pjaxReplace(url)
},
showBirthday() {
if (this.formData.birthday) {
this.$set(this.formData, "currentDate", new Date(this.formData.birthday))
}
this.showDatePicker = true
},
userCancel() {
if (!this.formData.userId) {
this.$set(this.formData, "userName", null)
}
this.subsidizedList = []
},
async initDictOptions() {
const subsidyTypeData = await this.$businessTool.getDictOptions("DIFFICULT_SUBSIDY_TYPE");
this.subsidyTypeColumns = subsidyTypeData.map(item => {
@@ -432,7 +895,7 @@ layout("/layouts/platform_h5.html"){
this.$axios.post("/platform/difficultHelp/apply/save", { data: JSON.stringify(submitData) }).then(res => {
if (res.code === 0) {
this.$toast("保存成功")
this.$pjaxReplace("/platform/difficultHelp/mine/h5")
this.clearPopupHistoryAndNavigate("/platform/difficultHelp/mine/h5")
}
})
})
@@ -465,7 +928,7 @@ layout("/layouts/platform_h5.html"){
}).then(res => {
if (res.code === 0) {
this.$toast("提交成功")
this.$pjaxReplace("/platform/difficultHelp/mine/h5")
this.clearPopupHistoryAndNavigate("/platform/difficultHelp/mine/h5")
}
})
})
@@ -500,39 +963,56 @@ layout("/layouts/platform_h5.html"){
}).then(res => {
if (res.code === 0) {
this.$toast("提交成功")
this.$pjaxReplace("/platform/difficultHelp/mine/h5")
this.clearPopupHistoryAndNavigate("/platform/difficultHelp/mine/h5")
}
})
})
})
},
// 查询受助人
async queryRecipients(key) {
/**
* 按姓名或工号查询受助人。
* recipientSearchKey 传搜索关键词;接口返回用户数组,列表仅展示姓名和单位。
*/
queryRecipients() {
if (this.formData.mode !== "2") {
return
}
const res = await this.$axios.post("/platform/difficultHelp/apply/queryRecipients", { key })
if (res.code === 0) {
res.data.forEach((v) => {
v.name = v.username
return v
})
this.subsidizedList = res.data
this.userSheetShow = true
const key = (this.recipientSearchKey || "").trim()
if (!key) {
this.$toast("请输入姓名或工号")
return
}
this.$axios.post("/platform/difficultHelp/apply/queryRecipients", {key: key}).then((res) => {
if (res.code === 0) {
const recipients = res.data || []
const options = recipients.map((item) => {
return {value: item.id, text: item.username, description: item.unitName, source: item}
})
const currentRecipient = options.find((item) => {
return String(item.value) === String(this.formData.userId || "")
})
this.$set(this, "recipientHasSearched", true)
this.$set(this, "subsidizedList", recipients)
this.$set(this, "optionPopupOptions", options)
this.$set(this, "pendingOptionValue", currentRecipient ? currentRecipient.value : "")
}
})
},
onUserSelect(val) {
const user = this.subsidizedList.find((item) => item.id === val.id)
// 受助人确认后回填用户、单位和工会信息,参数 user 为查询接口返回的完整用户对象。
applySelectedUser(user) {
if (user) {
const unitId = user.unitId !== undefined ? user.unitId : user.unitid
const unionId = user.unionId !== undefined ? user.unionId : user.unionid
this.$set(this.formData, "userId", user.id)
this.$set(this.formData, "userName", user.username)
this.$set(this.formData, "loginName", user.loginname)
this.$set(this.formData, "sex", user.sex)
this.$set(this.formData, "unitId", user.unitid)
this.$set(this.formData, "birthday", user.birthday)
this.$set(this.formData, "unitId", unitId)
this.$set(this.formData, "unitName", user.unitName)
this.$set(this.formData, "unionId", user.unionid)
this.$set(this.formData, "unionId", unionId)
this.$set(this.formData, "unionName", user.unionName)
this.$set(this.formData, "position", user.position || "")
this.$set(this.formData, "officialCapacity", user.officialCapacity || "")
@@ -554,27 +1034,11 @@ layout("/layouts/platform_h5.html"){
this.$set(this.formData, "idCard", null)
this.$set(this.formData, "homeAddress", null)
}
this.userSheetShow = false
},
onSexConfirm(value) {
this.formData.sex = value
this.showSexPicker = false
},
onDateConfirm(value) {
this.formData.birthday = moment(value).format("YYYY-MM-DD")
this.showDatePicker = false
},
onSubsidyTypeConfirm(option) {
this.$set(this.formData, "subsidyName", option.text);
this.subsidyValue = option.value;
this.showSubsidyPicker = false;
},
onSubsidyStandardConfirm(option) {
this.$set(this.formData, "subsidyStandardsName", option.text);
this.subsidyStandardsValue = option.value;
this.showSubsidyStandardPicker = false;
},
findOne(id) {
this.$axios.post("/platform/difficultHelp/mine/findOne", { id }).then(res => {
@@ -599,7 +1063,6 @@ layout("/layouts/platform_h5.html"){
}
}
this.queryRecipients(res.data.loginName)
}
})
},
@@ -631,7 +1094,6 @@ layout("/layouts/platform_h5.html"){
this.$set(this.formData, 'familyList', [])
this.$set(this.formData, 'files', [])
this.$set(this.formData, 'applyCount', savedApplyCount )
this.queryRecipients(user.loginname)
} else {
this.formData = {}
this.$set(this.formData, 'proxyUserId', user.id)
@@ -774,6 +1236,18 @@ layout("/layouts/platform_h5.html"){
this.modeChange("1")
}
}
},
mounted() {
// 使用布局统一管理器注册弹层状态,处理方式与提案预审核详情弹层保持一致。
if (!window.h5HistoryLayerManager) return
window.h5HistoryLayerManager.register(this.historyLayerPageKey, (stack) => {
this.syncHistoryLayerStack(stack)
})
},
beforeDestroy() {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.pageKey === this.historyLayerPageKey) {
window.h5HistoryLayerManager.unregister(this.historyLayerPageKey)
}
}
})
</script>
@@ -1,38 +1,81 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style id="style-difficult-help-approval-list-h5">
<!--#include("../common/approval-list.css"){}#-->
</style>
<style id="style-difficult-help-info-h5">
<!--#include("../common/info.css"){}#-->
</style>
<div id="app">
<van-sticky>
<div class="difficult-help-approval-page">
<van-nav-bar title="帮扶分工会审核" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<div class="difficult-help-approval-sticky">
<van-search
v-model="pageForm.searchKeyword"
class="difficult-help-approval-search"
placeholder="请输入工号或者姓名进行查询"
show-action
:reverse-color="false"
input-align="left"
@search="doSearch">
<template #action>
<div @click="doSearch">搜索</div>
</template>
shape="round"
clearable
@search="doSearch"
@clear="doSearch">
</van-search>
<van-dropdown-menu
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
class="difficult-help-approval-filter"
:class="{'is-open': filterOpened}"
active-color="#1989fa"
:close-on-click-outside="false"
:close-on-click-overlay="false"
>
<van-dropdown-item :options="unionList" @change="unionChange" v-model="pageForm.unionId"></van-dropdown-item>
<van-dropdown-item :options="unitList" @change="doSearch" v-model="pageForm.unitId"></van-dropdown-item>
<van-dropdown-item
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
:options="unionList"
@change="unionChange"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.unionId">
</van-dropdown-item>
<van-dropdown-item
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
:options="unitList"
@change="doSearch"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.unitId">
</van-dropdown-item>
<van-dropdown-item
:options="approvalOptions"
@change="onApprovalFilterChange"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.approvalText">
</van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.approvalText"
@change="(val)=>{this.pageForm.audit = val==='1';this.doSearch();}">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</div>
</van-sticky>
<table-list api="/platform/difficultHelp/branchUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName" @ready="onReady">
<main class="difficult-help-approval-content">
<div class="difficult-help-list-skeleton" v-if="skeletonLoading">
<div class="difficult-help-list-skeleton__card" v-for="index in 3" :key="'branch-approval-skeleton-' + index">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<table-list api="/platform/difficultHelp/branchUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName"
v-show="!skeletonLoading" :show_loading_overlay="false" @ready="onReady"
@loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="difficult-help-approval-card-header">
<div class="difficult-help-approval-card-heading">
<div class="difficult-help-approval-card-title">{{row.userName || '--'}}</div>
<div class="difficult-help-approval-card-code">工号:{{row.loginName || '--'}}</div>
</div>
<enum-tag class="difficult-help-approval-card-status" v-if="row.instanceState" :value="row.instanceState"
name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
</div>
</template>
<template v-slot="{index,row}">
<table-column label="受助人">{{row.userName + '(' + row.loginName + ')'}}</table-column>
<table-column label="申请人">{{row.proxyUserName || '--'}}</table-column>
<table-column label="所属工会">{{row.unionName}}</table-column>
<table-column label="工作单位">{{row.unitName}}</table-column>
<table-column label="困难类型">{{row.subsidyStandards}}</table-column>
@@ -41,43 +84,66 @@ layout("/layouts/platform_h5.html"){
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<van-icon name="eye-o"></van-icon>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
<i class="fa fa-check"></i>
<van-icon name="edit"></van-icon>
<span>审核</span>
</div>
<div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">
<i class="fa fa-undo"></i>
<van-icon name="revoke"></van-icon>
<span>撤回</span>
</div>
</template>
<template #empty>
<div class="difficult-help-approval-empty">
<img src="/assets/mobile/img/difficultHelp/difficult-help-empty.png" alt="暂无帮扶审核记录插画">
<div class="difficult-help-approval-empty__title">暂无帮扶审核记录</div>
<div class="difficult-help-approval-empty__hint">当前筛选条件下暂时没有帮扶申请</div>
</div>
</template>
</table-list>
</main>
<info ref="infoRef">
<info ref="infoRef" page-key="difficult-help-branch-union-detail" full-screen>
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<van-form ref="formRef">
<van-field
v-model="formData.tf_opinion"
name="tf_opinion"
label="审批意见"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
required
maxlength="100"
show-word-limit
></van-field>
</van-form>
<div style="display: flex; justify-content: space-between; column-gap: 10px; padding: 10px">
<van-button type="primary" block @click="handleTaskAction(1)">同意</van-button>
<van-button type="danger" block @click="handleTaskAction(2)">拒绝</van-button>
<van-button type="danger" block @click="handleTaskAction(6)">退回</van-button>
<section class="difficult-help-info-card">
<h2 class="difficult-help-info-card__title">
审批意见<span class="difficult-help-audit-card__required">*</span>
</h2>
<van-form ref="formRef">
<van-field
v-model="formData.tf_opinion"
class="difficult-help-audit-textarea"
name="tf_opinion"
type="textarea"
rows="6"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
maxlength="100"
show-word-limit
></van-field>
</van-form>
</section>
<div class="difficult-help-popup-actions">
<van-button class="difficult-help-action-danger" type="danger" block :loading="formLoading" @click="handleTaskAction(6)">退回</van-button>
<van-button class="difficult-help-action-danger" type="danger" block :loading="formLoading" @click="handleTaskAction(2)">拒绝</van-button>
<van-button class="difficult-help-action-primary" type="primary" block :loading="formLoading" @click="handleTaskAction(1)">同意</van-button>
</div>
</div>
</info>
<van-dialog
:value="confirmVisible"
title="提示"
:message="confirmActionType === 'revoke' ? '您确定要撤回吗?' : '您确定要提交吗?'"
show-cancel-button
confirm-button-color="#1989fa"
@confirm="confirmPendingAction"
@cancel="closeActionConfirm">
</van-dialog>
</div>
</div>
<script nonce="${cspNonce!}">
<!--#include("../common/info.js"){}#-->
@@ -88,8 +154,9 @@ layout("/layouts/platform_h5.html"){
data() {
return {
pageForm: {
pageNo: 1,
pageNumber: 1,
pageSize: 10,
totalCount: 0,
searchKeyword: '',
unionId: '',
unitId: '',
@@ -98,9 +165,21 @@ layout("/layouts/platform_h5.html"){
},
unionList: [],
unitList: [],
approvalOptions: [
{text: "未审核", value: "0"},
{text: "已审核", value: "1"}
],
filterOpened: false,
skeletonLoading: true,
formData: {},
showApprovalForm: false
showApprovalForm: false,
formLoading: false,
confirmVisible: false,
confirmActionType: "task",
pendingSubmitType: null,
pendingRevokeRow: null,
historyLayerUnsubscribe: null
}
},
components: {
@@ -109,6 +188,24 @@ layout("/layouts/platform_h5.html"){
methods: {
historyBack,
// state.initial 表示当前查询前列表为空,仅此阶段使用整组卡片骨架屏。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, "skeletonLoading", state.loading)
},
// 审核状态下拉值为字符串,接口参数 audit 继续使用布尔值。
onApprovalFilterChange(val) {
this.$set(this.pageForm, "audit", val === "1")
this.doSearch()
},
// 同步筛选面板展开状态,用于调整下拉框底部圆角。
onFilterOpen() {
this.$set(this, "filterOpened", true)
},
onFilterClose() {
this.$set(this, "filterOpened", false)
},
onReady() {
this.doSearch()
},
@@ -123,99 +220,139 @@ layout("/layouts/platform_h5.html"){
// 查看详情
onView(row) {
this.showApprovalForm = false
this.$refs.infoRef.onOpen(row)
this.$set(this, "showApprovalForm", false)
this.$refs.infoRef.onOpen(row, "帮扶详情")
},
onApproval(row) {
this.showApprovalForm = true
this.$refs.infoRef.onOpen(row)
this.formData = {
this.$set(this, "showApprovalForm", true)
this.$set(this, "formData", {
processTaskId: row.taskId,
taskName: row.curTaskName
}
taskName: row.curTaskName,
tf_opinion: ""
})
this.$refs.infoRef.onOpen(row, row.curTaskName || "帮扶审核")
},
async handleTaskAction(val) {
try {
await this.$refs.formRef.validate();
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
}).then(() => {
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val
})
}).then((res) => {
if (res.code === 0) {
this.$refs.infoRef.onClose()
// val 为流程提交类型:1 同意、2 拒绝、6 退回;校验通过后打开历史栈确认框。
handleTaskAction(val) {
this.$refs.formRef.validate()
.then(() => {
this.$set(this, "confirmActionType", "task")
this.$set(this, "pendingSubmitType", val)
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-action-confirm")) return
this.$set(this, "confirmVisible", true)
})
.catch(() => {})
},
// 确认操作时先回退确认框历史,再根据操作类型调用办理或撤回接口。
confirmPendingAction() {
this.closeActionConfirm(() => {
if (this.confirmActionType === "revoke") {
this.revokeTask()
return
}
this.submitTaskAction()
})
},
// 提交流程任务参数,接口成功后清理详情弹框历史并刷新列表。
submitTaskAction() {
this.$set(this, "formLoading", true)
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: this.pendingSubmitType
})
})
.then((res) => {
if (res && res.code === 0) {
const afterClose = () => {
this.$toast.success(res.msg)
this.doSearch()
}
})
}).catch(() => {
})
} catch (error) {
}
},
// 撤回
onRevoke(row) {
debugger
this.$dialog.confirm({
title: '提示',
message: '您确定要撤回吗?',
}).then(() => {
this.$axios.post('/flow/common/revokeTask', {taskId: row.taskId}).then(res => {
if (res.code === 0) {
this.$toast.success('撤回成功');
this.doSearch();
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.closeAll(afterClose)) return
this.$refs.infoRef.onClose(afterClose)
}
})
}).catch(() => {
});
.finally(() => {
this.$set(this, "formLoading", false)
})
},
closeActionConfirm(afterClose = null) {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.close("difficult-help-action-confirm", afterClose)) return
this.$set(this, "confirmVisible", false)
if (typeof afterClose === "function") afterClose()
},
// row 提供待撤回任务 taskId,确认后由统一确认框执行撤回。
onRevoke(row) {
this.$set(this, "confirmActionType", "revoke")
this.$set(this, "pendingRevokeRow", row)
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-action-confirm")) return
this.$set(this, "confirmVisible", true)
},
// 使用 pendingRevokeRow.taskId 撤回任务,接口返回统一响应对象。
revokeTask() {
if (!this.pendingRevokeRow) return
this.$set(this, "formLoading", true)
this.$axios.post("/flow/common/revokeTask", {taskId: this.pendingRevokeRow.taskId})
.then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg || "撤回成功")
this.doSearch()
}
})
.finally(() => {
this.$set(this, "formLoading", false)
})
},
unionChange() {
this.$set(this.pageForm, "unitId", null)
this.flushUnits()
this.doSearch()
},
async initUnion() {
initUnion() {
const hasAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
const unionId = hasAdmin ? null : this.$store.state.user.union.id
this.unionList = await this.$businessTool.listUnion(unionId)
this.unionList.forEach((v) => {
v.text = v.name
v.value = v.id
return this.$axios.post("/open/common/unionOptions", {unionId}, {h5SkeletonLoading: true}).then((res) => {
const list = res && res.code === 0 ? res.data : []
this.$set(this, "unionList", list || [])
this.unionList.forEach((v) => {
this.$set(v, "text", v.name)
this.$set(v, "value", v.id)
})
if (hasAdmin) this.unionList.unshift({text: "全部工会", value: null})
if (this.unionList.length > 0) this.$set(this.pageForm, "unionId", this.unionList[0].value)
})
if (hasAdmin) {
this.unionList.unshift({ text: "全部工会", value: null })
}
if (this.unionList && this.unionList.length > 0) {
this.$set(this.pageForm, "unionId", this.unionList[0].value)
}
},
async flushUnits() {
flushUnits() {
const hasAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
const unionId = hasAdmin ? this.pageForm.unionId : this.$store.state.user.union.id
this.unitList = await this.$businessTool.listUnit(unionId)
this.unitList.forEach((v) => {
v.text = v.name
v.value = v.id
return this.$axios.post("/open/common/unitOptions", {unionId}, {h5SkeletonLoading: true}).then((res) => {
const list = res && res.code === 0 ? res.data : []
this.$set(this, "unitList", list || [])
this.unitList.forEach((v) => {
this.$set(v, "text", v.name)
this.$set(v, "value", v.id)
})
this.unitList.unshift({text: "全部单位", value: null})
if (this.unitList.length > 0) this.$set(this.pageForm, "unitId", this.unitList[0].value)
})
this.unitList.unshift({ text: "全部单位", value: null })
if (this.unitList && this.unitList.length > 0) {
this.$set(this.pageForm, "unitId", this.unitList[0].value)
}
},
},
async created() {
await this.initUnion()
await this.flushUnits()
created() {
this.initUnion().then(() => this.flushUnits())
if (!window.h5HistoryLayerManager) return
this.$set(this, "historyLayerUnsubscribe", window.h5HistoryLayerManager.subscribe((stack) => {
this.$set(this, "confirmVisible", stack.includes("difficult-help-action-confirm"))
}))
},
beforeDestroy() {
if (typeof this.historyLayerUnsubscribe === "function") this.historyLayerUnsubscribe()
}
})
</script>
@@ -0,0 +1,307 @@
#app {
--color-primary: #1989fa;
}
.difficult-help-approval-page {
min-height: 100vh;
padding-bottom: calc(24px + env(safe-area-inset-bottom));
box-sizing: border-box;
background: #f3f7fd;
}
.difficult-help-approval-page .van-nav-bar,
.difficult-help-approval-page .van-sticky--fixed {
background: #fff;
}
.difficult-help-approval-page .van-nav-bar__title {
color: #172033;
font-size: 16px;
font-weight: 600;
}
.difficult-help-approval-page .van-nav-bar .van-icon,
.difficult-help-approval-page .van-nav-bar__text {
color: #1989fa;
}
.difficult-help-approval-sticky {
padding: 22px 12px 1px;
box-sizing: border-box;
background: #f3f7fd;
}
.difficult-help-approval-search {
margin: 0 0 8px;
padding: 0;
overflow: hidden;
border-radius: 12px;
background: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
}
.difficult-help-approval-search .van-search__content {
height: 42px;
padding-left: 12px;
align-items: center;
border-radius: 12px;
background: #fff;
}
.difficult-help-approval-search .van-field__control {
color: #263548;
font-size: 14px;
}
.difficult-help-approval-search .van-field__control::placeholder {
color: #a7b0bf;
}
.difficult-help-approval-filter {
margin-bottom: 10px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
transition: border-radius .18s ease;
}
.difficult-help-approval-filter.is-open {
border-radius: 10px 10px 0 0;
}
.difficult-help-approval-filter .van-dropdown-menu__bar,
.difficult-help-approval-filter .van-dropdown-menu__item,
.difficult-help-approval-filter .van-dropdown-menu__title {
height: 44px;
}
.difficult-help-approval-filter .van-dropdown-menu__bar {
box-shadow: none;
}
.difficult-help-approval-filter .van-dropdown-menu__item {
min-width: 0;
align-items: center;
justify-content: center;
}
.difficult-help-approval-filter .van-dropdown-menu__title {
display: inline-flex;
max-width: calc(100% - 12px);
align-items: center;
color: #66758a;
font-size: 13px;
line-height: 20px;
}
.difficult-help-approval-filter .van-dropdown-menu__title--active,
.difficult-help-approval-filter .van-dropdown-item__option--active,
.difficult-help-approval-filter .van-dropdown-item__option--active .van-cell__title,
.difficult-help-approval-filter .van-dropdown-item__option--active .van-dropdown-item__icon {
color: #1989fa;
}
.difficult-help-approval-filter .van-dropdown-item__content {
width: calc(100% - 24px);
max-height: 50%;
margin: 0 12px;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
box-sizing: border-box;
border-radius: 0 0 12px 12px;
box-shadow: 0 10px 24px rgba(31, 65, 108, .14);
}
.difficult-help-approval-filter .van-dropdown-item__option {
display: flex;
min-height: 48px;
padding: 0 16px;
align-items: center;
color: #46566c;
font-size: 14px;
}
.difficult-help-approval-filter .van-dropdown-item__option .van-cell__title {
display: flex;
min-width: 0;
min-height: 20px;
align-items: center;
flex: 1 1 auto;
line-height: 20px;
}
.difficult-help-approval-filter .van-dropdown-item__option .van-cell__value {
display: flex;
width: 20px;
min-width: 0;
overflow: visible;
align-items: center;
justify-content: flex-end;
flex: 0 0 20px;
}
.difficult-help-approval-content {
padding: 0 12px;
}
.difficult-help-list-skeleton {
padding-bottom: 2px;
}
.difficult-help-list-skeleton__card {
margin-bottom: 12px;
padding: 14px;
overflow: hidden;
box-sizing: border-box;
border: 1px solid #e9eff7;
border-radius: 12px;
background: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
}
.difficult-help-list-skeleton__card .van-skeleton {
padding: 0;
}
.difficult-help-list-skeleton__card .van-skeleton__title {
width: 34%;
height: 17px;
margin-bottom: 13px;
}
.difficult-help-list-skeleton__card .van-skeleton__row {
height: 12px;
margin-top: 11px;
}
.difficult-help-list-skeleton__card .van-skeleton__row:nth-child(2n) {
width: 88%;
}
.difficult-help-list-skeleton__card .van-skeleton__row:last-child {
width: 48%;
margin-top: 18px;
}
.difficult-help-approval-page .table-list-container {
margin-top: 0;
padding-bottom: 2px;
}
.difficult-help-approval-page .table-list-container .table-list-item {
margin-bottom: 12px;
padding: 14px;
border: 1px solid #e9eff7;
border-radius: 12px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
}
.difficult-help-approval-card-header {
display: flex;
margin-bottom: 8px;
align-items: flex-start;
justify-content: space-between;
}
.difficult-help-approval-card-heading {
min-width: 0;
padding-right: 10px;
flex: 1;
}
.difficult-help-approval-card-title {
overflow: hidden;
color: #253247;
font-size: 15px;
font-weight: 600;
line-height: 22px;
text-overflow: ellipsis;
white-space: nowrap;
}
.difficult-help-approval-card-code {
margin-top: 1px;
color: #9ba6b6;
font-size: 12px;
line-height: 18px;
}
.difficult-help-approval-card-status {
display: inline-flex;
flex: none;
}
.difficult-help-approval-card-status .van-tag {
min-height: 24px;
padding: 3px 9px;
border: 0;
border-radius: 12px;
box-sizing: border-box;
font-size: 12px;
line-height: 18px;
}
.difficult-help-approval-page .table-list-container .table-list-item .item-actions {
margin-top: 10px;
padding-top: 10px;
border-top-color: #edf1f6;
}
.difficult-help-approval-page .table-list-container .table-list-item .action-btn {
min-height: 28px;
padding: 4px 11px;
border-radius: 14px;
color: #1989fa;
background: #eaf4ff;
font-size: 12px;
line-height: 18px;
}
.difficult-help-approval-page .table-list-container .table-list-item .action-btn.delete {
color: #f04444;
background: #fff0f0;
}
.difficult-help-approval-page .table-list-container .table-list-item .action-btn .van-icon {
margin-right: 5px;
font-size: 13px;
}
.difficult-help-approval-page .empty-state {
padding: 0;
}
.difficult-help-approval-empty {
display: flex;
min-height: 280px;
padding: 46px 18px 36px;
align-items: center;
justify-content: center;
box-sizing: border-box;
flex-direction: column;
text-align: center;
}
.difficult-help-approval-empty img {
display: block;
width: 86%;
max-width: 290px;
height: auto;
object-fit: contain;
}
.difficult-help-approval-empty__title {
margin-top: 14px;
color: #50627a;
font-size: 15px;
font-weight: 600;
line-height: 22px;
}
.difficult-help-approval-empty__hint {
margin-top: 5px;
color: #9aa7b8;
font-size: 12px;
line-height: 18px;
}
@@ -0,0 +1,353 @@
.difficult-help-full-popup {
--color-primary: #1989fa;
width: 100%;
height: 100%;
overflow: hidden;
background: #f3f7fc;
}
.difficult-help-full-popup__page {
display: flex;
height: 100%;
padding-top: 46px;
overflow: hidden;
box-sizing: border-box;
flex-direction: column;
}
.difficult-help-full-popup__page > .van-nav-bar {
z-index: 20;
background: #fff;
}
.difficult-help-full-popup__page > .van-nav-bar .van-icon,
.difficult-help-full-popup__page > .van-nav-bar .van-nav-bar__text {
color: #1989fa;
}
.difficult-help-full-popup__scroll {
min-height: 0;
overflow-y: auto;
flex: 1;
-webkit-overflow-scrolling: touch;
}
.difficult-help-info-main {
padding: 12px 15px calc(18px + env(safe-area-inset-bottom));
}
.difficult-help-info-card {
margin-bottom: 11px;
overflow: hidden;
border: 1px solid #e9eff7;
border-radius: 10px;
background: #fff;
box-shadow: 0 3px 11px rgba(51, 87, 126, .08);
}
.difficult-help-info-card__title {
position: relative;
margin: 0;
padding: 12px 14px 8px 21px;
color: #354255;
font-size: 15px;
font-weight: 600;
line-height: 22px;
}
.difficult-help-info-card__title::before {
position: absolute;
top: 15px;
bottom: 11px;
left: 12px;
width: 2px;
border-radius: 2px;
background: #1989fa;
content: "";
}
.difficult-help-audit-card__required {
margin-left: 4px;
color: #ee0a24;
font-size: 14px;
}
.difficult-help-audit-form {
padding-bottom: 4px;
}
.difficult-help-info-card .van-field.difficult-help-audit-select,
.difficult-help-info-card .van-field.difficult-help-audit-money {
min-height: 48px;
padding: 11px 14px;
}
.difficult-help-info-card .van-field.difficult-help-audit-select .van-field__label,
.difficult-help-info-card .van-field.difficult-help-audit-money .van-field__label {
width: 88px;
color: #596779;
font-size: 12px;
}
.difficult-help-info-card .van-field.difficult-help-audit-select .van-field__control,
.difficult-help-info-card .van-field.difficult-help-audit-money .van-field__control {
color: #253247;
font-size: 13px;
}
.difficult-help-info-card .van-field.difficult-help-audit-textarea {
width: calc(100% - 28px);
margin: 0 14px 14px;
padding: 12px;
box-sizing: border-box;
border: 1px solid #c9d5e5;
border-radius: 10px;
background: #f7f9fd;
}
.difficult-help-info-card .van-field.difficult-help-audit-textarea .van-field__control {
min-height: 132px;
color: #253247;
font-size: 13px;
line-height: 22px;
}
.difficult-help-info-card .van-field.difficult-help-audit-textarea .van-field__control::placeholder,
.difficult-help-info-card .van-field.difficult-help-audit-select .van-field__control::placeholder,
.difficult-help-info-card .van-field.difficult-help-audit-money .van-field__control::placeholder {
color: #9aa7b8;
}
.difficult-help-info-card .van-field.difficult-help-audit-textarea .van-field__word-limit {
color: #718096;
}
.difficult-help-info-card .van-field.difficult-help-audit-textarea::after {
display: none;
}
.difficult-help-popup-actions {
display: flex;
padding: 10px 0 calc(12px + env(safe-area-inset-bottom));
justify-content: space-between;
column-gap: 10px;
background: transparent;
}
.difficult-help-popup-actions .van-button {
min-width: 0;
border-radius: 14px;
}
.difficult-help-popup-actions .difficult-help-action-primary {
border-color: #1989fa;
background: #1989fa;
}
.difficult-help-popup-actions .difficult-help-action-danger {
border-color: #ff4d4f;
background: #ff4d4f;
}
.difficult-help-info-card .van-cell-group {
margin: 0;
background: transparent;
}
.difficult-help-info-card .van-cell {
min-height: 44px;
padding: 10px 14px;
background: transparent;
}
.difficult-help-info-card .van-cell:not(:last-child)::after {
right: 14px;
left: 14px;
border-color: #eef2f7;
}
.difficult-help-info-card .van-cell__title {
width: 112px;
color: #596779;
font-size: 12px;
line-height: 24px;
flex: 0 0 112px;
}
.difficult-help-info-card .van-cell__value {
min-width: 0;
color: #4f5d70;
font-size: 12px;
line-height: 24px;
text-align: left;
overflow-wrap: anywhere;
}
.difficult-help-info-long-cell {
display: block;
}
.difficult-help-info-long-cell .van-cell__title {
width: auto;
max-width: 100%;
}
.difficult-help-info-long-cell .van-cell__value {
margin-top: 7px;
}
.difficult-help-info-pre-line {
white-space: pre-line;
overflow-wrap: anywhere;
}
.difficult-help-family-list {
padding: 0 14px 14px;
}
.difficult-help-family-item {
overflow: hidden;
border: 1px solid #e5edf7;
border-radius: 10px;
background: #fff;
box-shadow: 0 2px 8px rgba(51, 87, 126, .06);
}
.difficult-help-family-item + .difficult-help-family-item {
margin-top: 10px;
}
.difficult-help-family-header {
display: flex;
min-height: 44px;
padding: 9px 11px;
align-items: center;
box-sizing: border-box;
background: linear-gradient(90deg, #f0f6ff 0%, #f8fbff 100%);
}
.difficult-help-family-index {
display: inline-flex;
width: 24px;
height: 24px;
margin-right: 8px;
align-items: center;
justify-content: center;
flex: 0 0 24px;
border-radius: 50%;
color: #fff;
background: #1989fa;
font-size: 12px;
font-weight: 600;
}
.difficult-help-family-name {
min-width: 0;
overflow: hidden;
color: #2f3d51;
font-size: 14px;
font-weight: 600;
line-height: 22px;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1;
}
.difficult-help-family-relation {
max-width: 92px;
margin-left: 8px;
padding: 2px 8px;
overflow: hidden;
border-radius: 10px;
color: #1989fa;
background: #e7f2ff;
font-size: 11px;
line-height: 18px;
text-overflow: ellipsis;
white-space: nowrap;
flex: none;
}
.difficult-help-family-summary {
display: grid;
margin: 0 11px;
padding: 11px 0;
grid-template-columns: repeat(2, minmax(0, 1fr));
border-bottom: 1px solid #eef2f7;
}
.difficult-help-family-summary__item {
min-width: 0;
padding: 0 8px;
}
.difficult-help-family-summary__item:first-child {
padding-left: 0;
border-right: 1px solid #eef2f7;
}
.difficult-help-family-summary__item span,
.difficult-help-family-summary__item strong,
.difficult-help-family-detail__row span,
.difficult-help-family-detail__row strong {
display: block;
overflow-wrap: anywhere;
}
.difficult-help-family-summary__item span,
.difficult-help-family-detail__row span {
color: #909bac;
font-size: 11px;
line-height: 17px;
}
.difficult-help-family-summary__item strong {
margin-top: 3px;
color: #435167;
font-size: 13px;
font-weight: 500;
line-height: 20px;
}
.difficult-help-family-detail {
padding: 4px 11px 7px;
}
.difficult-help-family-detail__row {
display: grid;
padding: 7px 0;
grid-template-columns: 68px minmax(0, 1fr);
column-gap: 8px;
}
.difficult-help-family-detail__row strong {
color: #536176;
font-size: 12px;
font-weight: 400;
line-height: 18px;
}
.difficult-help-info-card .van-empty {
padding: 12px 0 20px;
}
.difficult-help-info-promise {
color: #4f5d70;
font-size: 12px;
line-height: 21px;
}
.difficult-help-info-commitment {
display: flex;
margin-top: 8px;
align-items: flex-end;
color: #8995a6;
font-size: 11px;
line-height: 18px;
flex-direction: column;
}
.difficult-help-signature-image {
display: block;
max-width: 100%;
}
@@ -1,166 +1,326 @@
const INFO = {
template: /*language=HTML*/ `
<van-action-sheet v-model="visible" title="查看详情">
<div class="detail-container">
<van-cell-group title="基本信息">
<van-cell title="填写人姓名">{{ viewData.proxyUserName }}</van-cell>
<van-cell title="填写人工号">{{ viewData.proxyLoginName }}</van-cell>
<van-cell title="性别">{{ viewData.sex }}</van-cell>
<component
:is="detailComponent"
v-model="visible"
:title="detailTitle"
:position="detailPosition"
:class="detailClass"
:close-on-click-overlay="false"
:get-container="detailContainer"
>
<div :class="{'difficult-help-full-popup__page': fullScreen, 'detail-container': !fullScreen}">
<van-nav-bar
v-if="fullScreen"
:title="popupTitle"
left-arrow
left-text="返回"
fixed
@click-left="onClose()"
></van-nav-bar>
<div ref="detailScrollRef" :class="{'difficult-help-full-popup__scroll': fullScreen}">
<main :class="{'difficult-help-info-main': fullScreen}">
<section :class="{'difficult-help-info-card': fullScreen}">
<h2 v-if="fullScreen" class="difficult-help-info-card__title">基本信息</h2>
<van-cell-group :title="fullScreen ? '' : '基本信息'" :border="!fullScreen">
<van-cell title="填写人姓名">{{ viewData.proxyUserName || '--' }}</van-cell>
<van-cell title="填写人工号">{{ viewData.proxyLoginName || '--' }}</van-cell>
<van-cell title="性别">{{ viewData.sex || '--' }}</van-cell>
<van-cell title="受助人">
{{ viewData.userName || '--' }}{{ viewData.loginName ? '' + viewData.loginName + '' : '' }}
</van-cell>
<van-cell title="所在单位">{{ viewData.unitName || '--' }}</van-cell>
<van-cell title="所在工会">{{ viewData.unionName || '--' }}</van-cell>
<van-cell title="职务">{{ viewData.position || '--' }}</van-cell>
<van-cell title="职称">{{ viewData.officialCapacity || '--' }}</van-cell>
<van-cell title="联系电话">{{ viewData.mobile || '--' }}</van-cell>
<van-cell title="家庭年总收入(万元)">{{ viewData.homeIncome || '--' }}</van-cell>
<van-cell title="家庭人数">{{ viewData.homeNumber || '--' }}</van-cell>
<van-cell :class="{'difficult-help-info-long-cell': fullScreen}" title="家庭住址">
{{ viewData.homeAddress || '--' }}
</van-cell>
<van-cell title="补助类型">
<dict-tag :options="dict.type.DIFFICULT_SUBSIDY_TYPE" :value="viewData.subsidy"></dict-tag>
</van-cell>
<van-cell title="困难类型">
<dict-tag :options="dict.type.DIFFICULT_TYPE" :value="viewData.subsidyStandards"></dict-tag>
</van-cell>
<van-cell title="申请次数">{{ viewData.applyCount || 1 }}</van-cell>
<van-cell :class="{'difficult-help-info-long-cell': fullScreen}" title="开户行">
{{ viewData.bankOfDeposit || '--' }}
</van-cell>
<van-cell title="银行卡号">{{ viewData.bankCardNum || '--' }}</van-cell>
<van-cell :class="{'difficult-help-info-long-cell': fullScreen, 'text-cell': !fullScreen}" title="生活致困原因">
<div class="difficult-help-info-pre-line">{{ viewData.reason || '暂无' }}</div>
</van-cell>
</van-cell-group>
</section>
<van-cell title="受助人">{{ viewData.userName + '(' + viewData.loginName + ')' }}</van-cell>
<van-cell title="所在单位">{{ viewData.unitName }}</van-cell>
<van-cell title="所在工会">{{ viewData.unionName }}</van-cell>
<van-cell title="职务">{{ viewData.position }}</van-cell>
<van-cell title="职称">{{ viewData.officialCapacity }}</van-cell>
<van-cell title="联系电话">{{ viewData.mobile }}</van-cell>
<van-cell title="家庭年总收入(万元)">{{ viewData.homeIncome }}</van-cell>
<van-cell title="家庭人数">{{ viewData.homeNumber }}</van-cell>
<van-cell title="家庭住址">{{ viewData.homeAddress }}</van-cell>
<van-cell title="补助类型">
<dict-tag :options="dict.type.DIFFICULT_SUBSIDY_TYPE" :value="viewData.subsidy"></dict-tag>
</van-cell>
<van-cell title="困难类型">
<dict-tag :options="dict.type.DIFFICULT_TYPE" :value="viewData.subsidyStandards"></dict-tag>
</van-cell>
<van-cell title="申请次数">{{ viewData.applyCount || 1 }}</van-cell>
<van-cell title="开户行">{{ viewData.bankOfDeposit }}</van-cell>
<van-cell title="银行卡号">{{ viewData.bankCardNum }}</van-cell>
<van-cell title="生活致困原因" class="text-cell">
<template #label>
<div style="word-wrap: break-word; white-space: pre-line;">
{{viewData.reason}}
<section v-if="fullScreen" class="difficult-help-info-card">
<h2 class="difficult-help-info-card__title">家庭成员</h2>
<div v-if="viewData.familyList && viewData.familyList.length" class="difficult-help-family-list">
<article
v-for="(item, index) in viewData.familyList"
:key="index"
class="difficult-help-family-item"
>
<header class="difficult-help-family-header">
<span class="difficult-help-family-index">{{ index + 1 }}</span>
<strong class="difficult-help-family-name">{{ item.name || '未填写姓名' }}</strong>
<span class="difficult-help-family-relation">{{ item.relation || '关系未填写' }}</span>
</header>
<div class="difficult-help-family-summary">
<div class="difficult-help-family-summary__item">
<span>年龄</span>
<strong>
{{ item.age !== null && item.age !== undefined && item.age !== '' ? item.age + ' 岁' : '--' }}
</strong>
</div>
<div class="difficult-help-family-summary__item">
<span>年收入</span>
<strong>
{{ item.yearIncome !== null && item.yearIncome !== undefined && item.yearIncome !== '' ? item.yearIncome + ' 元' : '--' }}
</strong>
</div>
</div>
<div class="difficult-help-family-detail">
<div class="difficult-help-family-detail__row">
<span>工作单位</span>
<strong>{{ item.homeUnit || '--' }}</strong>
</div>
<div class="difficult-help-family-detail__row">
<span>备注</span>
<strong>{{ item.remarks || '--' }}</strong>
</div>
</div>
</article>
</div>
</template>
</van-cell>
<van-empty v-else description="暂无家庭成员信息" image-size="72"></van-empty>
</section>
<van-cell title="家庭成员" class="column-cell">
<table class="family-table">
<thead>
<tr>
<th class="table-header">序号</th>
<th class="table-header">与本人关系</th>
<th class="table-header">姓名</th>
<th class="table-header">年龄</th>
<th class="table-header">工作单位</th>
<th class="table-header">年收入()</th>
<th class="table-header">备注</th>
</tr>
</thead>
<tbody>
<tr class="table-row" v-for="(item, index) in viewData.familyList" :key="index">
<td class="table-cell">{{ index + 1 }}</td>
<td class="table-cell">{{ item.relation }}</td>
<td class="table-cell">{{ item.name }}</td>
<td class="table-cell">{{ item.age }}</td>
<td class="table-cell">{{ item.homeUnit }}</td>
<td class="table-cell">{{ item.yearIncome }}</td>
<td class="table-cell">{{ item.remarks }}</td>
</tr>
</tbody>
</table>
</van-cell>
<van-cell-group v-else title="家庭成员">
<van-cell class="column-cell">
<table class="family-table">
<thead>
<tr>
<th class="table-header">序号</th>
<th class="table-header">与本人关系</th>
<th class="table-header">姓名</th>
<th class="table-header">年龄</th>
<th class="table-header">工作单位</th>
<th class="table-header">年收入()</th>
<th class="table-header">备注</th>
</tr>
</thead>
<tbody>
<tr class="table-row" v-for="(item, index) in viewData.familyList" :key="index">
<td class="table-cell">{{ index + 1 }}</td>
<td class="table-cell">{{ item.relation }}</td>
<td class="table-cell">{{ item.name }}</td>
<td class="table-cell">{{ item.age }}</td>
<td class="table-cell">{{ item.homeUnit }}</td>
<td class="table-cell">{{ item.yearIncome }}</td>
<td class="table-cell">{{ item.remarks }}</td>
</tr>
</tbody>
</table>
</van-cell>
</van-cell-group>
<van-cell title="本人承诺">
<div style="text-align: left; padding: 5px 0; font-size: 14px;">
本人及家庭成员未有购买价格超过15万元的机动车也未有两套及以上商品房特此承诺
</div>
<div style="text-align: right; font-size: 12px; color: #999; margin-top: 5px;">
<span>承诺人{{ viewData.userName }}</span>
<br/>
<span>{{ $moment(viewData.applyTime).format("YYYY-MM-DD") }}</span>
</div>
</van-cell>
<section :class="{'difficult-help-info-card': fullScreen}">
<h2 v-if="fullScreen" class="difficult-help-info-card__title">承诺与材料</h2>
<van-cell-group :title="fullScreen ? '' : '承诺与材料'" :border="!fullScreen">
<van-cell :class="{'difficult-help-info-long-cell': fullScreen}" title="本人承诺">
<div class="difficult-help-info-promise">
本人及家庭成员未有购买价格超过15万元的机动车也未有两套及以上商品房特此承诺
</div>
<div class="difficult-help-info-commitment">
<span>承诺人{{ viewData.userName || '--' }}</span>
<span>{{ viewData.applyTime ? $moment(viewData.applyTime).format('YYYY-MM-DD') : '--' }}</span>
</div>
</van-cell>
<van-cell :class="{'difficult-help-info-long-cell': fullScreen}" title="签字">
<van-image
v-if="viewData.sign"
:src="viewData.sign"
class="difficult-help-signature-image"
></van-image>
<span v-else>暂无签字</span>
</van-cell>
<van-cell :class="{'difficult-help-info-long-cell': fullScreen}" title="附件">
<file-preview v-if="fullScreen" :files="viewData.files" complete_result></file-preview>
<template v-else-if="viewData.files && viewData.files.length">
<div v-for="(file, index) in viewData.files" :key="index">
<a :href="file.url" target="_blank">{{ file.name }}</a>
</div>
</template>
<span v-else>暂无</span>
</van-cell>
</van-cell-group>
</section>
<van-cell title="签字" v-if="viewData.sign">
<img :src="viewData.sign" style="max-width: 100px; max-height: 50px;" />
</van-cell>
<van-cell title="附件" v-if="viewData.files && viewData.files.length > 0">
<div v-for="(file, index) in viewData.files" :key="index" style="margin-bottom: 5px;">
<a :href="file.url" target="_blank">{{ file.name }}</a>
</div>
</van-cell>
<van-cell title="附件" v-else>
<span>暂无</span>
</van-cell>
</van-cell-group>
<template v-for="task in doneTasks">
<van-cell-group :title="task.displayName" v-if="task.ext.isFirstTaskNode">
<van-cell title="申请用户">{{ task.ext.initiatorName }}({{task.ext.initiatorAccount}})
</van-cell>
<van-cell title="申请时间">{{ task.finishTime }}</van-cell>
<van-cell title="办理结果">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
:value="task.ext.submitType"></dict-tag>
</van-cell>
</van-cell-group>
<van-cell-group :title="task.displayName" v-else>
<van-cell title="办理用户">{{ task.taskFormData.userName }}({{task.taskFormData.loginName}})
</van-cell>
<van-cell title="办理时间">{{ task.finishTime }}</van-cell>
<van-cell title="审批级别" v-if="task.displayName == '校工会审核'">
{{ viewData.level }}
</van-cell>
<van-cell title="审批金额" v-if="task.displayName == '校工会审核'">
{{ '¥' + viewData.money }}
</van-cell>
<van-cell title="办理结果">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
:value="task.ext.submitType"></dict-tag>
</van-cell>
<van-cell title="办理意见" v-if="!task.ext.isFirstTaskNode && task.taskFormData.opinion" class="direction-column-cell">
<div style="white-space: pre-line;">{{ task.taskFormData.opinion }}</div>
</van-cell>
</van-cell-group>
</template>
<section
v-for="(task, index) in doneTasks"
:key="task.id || index"
:class="{'difficult-help-info-card': fullScreen}"
>
<h2 v-if="fullScreen" class="difficult-help-info-card__title">
{{ task.displayName || '办理记录' }}
</h2>
<van-cell-group
v-if="task.ext && task.ext.isFirstTaskNode"
:title="fullScreen ? '' : task.displayName"
:border="!fullScreen"
>
<van-cell title="申请用户">
{{ task.ext.initiatorName || '--' }}{{ task.ext.initiatorAccount ? '' + task.ext.initiatorAccount + '' : '' }}
</van-cell>
<van-cell title="申请时间">{{ task.finishTime || '--' }}</van-cell>
<van-cell title="办理结果">
<dict-tag
:options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
:value="task.ext.submitType"
></dict-tag>
</van-cell>
</van-cell-group>
<van-cell-group v-else :title="fullScreen ? '' : task.displayName" :border="!fullScreen">
<van-cell title="办理用户">
{{ task.taskFormData && task.taskFormData.userName || '--' }}{{ task.taskFormData && task.taskFormData.loginName ? '' + task.taskFormData.loginName + '' : '' }}
</van-cell>
<van-cell title="办理时间">{{ task.finishTime || '--' }}</van-cell>
<van-cell v-if="task.displayName === '校工会审核'" title="审批级别">
{{ viewData.level || '--' }}
</van-cell>
<van-cell v-if="task.displayName === '校工会审核'" title="审批金额">
{{ viewData.money ? '¥' + viewData.money : '--' }}
</van-cell>
<van-cell title="办理结果">
<dict-tag
:options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
:value="task.ext && task.ext.submitType"
></dict-tag>
</van-cell>
<van-cell
v-if="task.taskFormData && (task.taskFormData.opinion || task.taskFormData.tf_opinion)"
:class="{'difficult-help-info-long-cell': fullScreen, 'direction-column-cell': !fullScreen}"
title="办理意见"
>
<div class="difficult-help-info-pre-line">
{{ task.taskFormData.opinion || task.taskFormData.tf_opinion }}
</div>
</van-cell>
</van-cell-group>
</section>
<slot></slot>
</main>
</div>
</div>
<slot></slot>
</van-action-sheet>
</component>
`,
dicts: ["PROCESS_TASK_SUBMIT_TYPE", "DIFFICULT_SUBSIDY_TYPE", "DIFFICULT_TYPE"], data() {
dicts: ["PROCESS_TASK_SUBMIT_TYPE", "DIFFICULT_SUBSIDY_TYPE", "DIFFICULT_TYPE"],
props: {
fullScreen: {
type: Boolean,
default: false
},
pageKey: {
type: String,
default: ""
}
},
data() {
return {
visible: false,
popupTitle: "帮扶详情",
row: {},
viewData: {},
doneTasks: [],
historyLayerUnsubscribe: null
}
},
computed: {
detailComponent() {
return this.fullScreen ? "van-popup" : "van-action-sheet"
},
detailTitle() {
return this.fullScreen ? undefined : "查看详情"
},
detailPosition() {
return this.fullScreen ? "right" : undefined
},
detailClass() {
return this.fullScreen ? "difficult-help-full-popup" : ""
},
detailContainer() {
return this.fullScreen ? "#app" : undefined
}
},
methods: {
onOpen(row) {
this.row = row
this.visible = true
this.info()
this.getDoneTasks()
// 浏览器历史变化时,只同步当前困难帮扶详情弹框的显示状态。
syncHistoryLayerStack(stack) {
this.$set(this, "visible", stack.includes("difficult-help-detail"))
},
// 关闭
onClose(){
this.visible = false
// 全屏详情复用同一滚动容器,每次切换记录后将内部滚动位置恢复到基本信息顶部。
resetScrollPosition() {
if (!this.fullScreen) return
this.$nextTick(() => {
const scrollContainer = this.$refs.detailScrollRef
if (scrollContainer) scrollContainer.scrollTop = 0
})
},
/**
* 打开困难帮扶详情
* row 传列表当前行title 传顶部导航标题方法无返回值详情数据和流程记录由组件内部加载
*/
onOpen(row, title = "帮扶详情") {
this.$set(this, "row", row || {})
this.$set(this, "popupTitle", title)
this.$set(this, "viewData", {})
this.$set(this, "doneTasks", [])
this.info()
this.getDoneTasks()
this.resetScrollPosition()
if (this.fullScreen && window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-detail")) return
this.$set(this, "visible", true)
},
// 顶部返回按钮与业务主动关闭统一回退详情弹框对应的浏览器历史。
onClose(afterClose = null) {
if (this.fullScreen && window.h5HistoryLayerManager && window.h5HistoryLayerManager.close("difficult-help-detail", afterClose)) return
this.$set(this, "visible", false)
if (typeof afterClose === "function") afterClose()
},
info() {
this.$axios.post("/platform/difficultHelp/mine/findOne", { id: this.row.id }).then((res) => {
this.$axios.post("/platform/difficultHelp/mine/findOne", {id: this.row.id}).then((res) => {
if (res.code === 0) {
this.viewData = res.data
this.$set(this, "viewData", res.data || {})
}
})
},
getDoneTasks() {
this.$axios.post("/flow/common/doneTasks", { bizId: this.row.id }).then((res) => {
this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => {
if (res.code === 0) {
this.doneTasks = res.data
this.$set(this, "doneTasks", res.data || [])
}
})
},
}
},
created() {
if (!this.fullScreen || !this.pageKey || !window.h5HistoryLayerManager) return
window.h5HistoryLayerManager.ensureRegistered(this.pageKey)
this.$set(this, "historyLayerUnsubscribe", window.h5HistoryLayerManager.subscribe((stack) => {
this.syncHistoryLayerStack(stack)
}))
},
beforeDestroy() {
if (typeof this.historyLayerUnsubscribe === "function") this.historyLayerUnsubscribe()
if (this.fullScreen && window.h5HistoryLayerManager && window.h5HistoryLayerManager.pageKey === this.pageKey) {
window.h5HistoryLayerManager.unregister(this.pageKey)
}
},
style: /*language=CSS*/ `
.column-cell {
@@ -169,21 +329,21 @@ const INFO = {
.family-table {
width: 100%;
max-width: 100%;
margin: 20px 0;
border-collapse: collapse;
table-layout: auto;
font-family: Arial, sans-serif;
text-align: center;
font-family: 'Arial', sans-serif;
margin: 20px 0;
}
.table-header {
background-color: gray;
color: white;
background-color: gray;
font-weight: bold;
text-transform: uppercase;
}
.table-row {
background-color: #f9f9f9;
transition: background-color 0.3s;
transition: background-color .3s;
}
.table-row:hover {
background-color: #f1f1f1;
@@ -191,8 +351,8 @@ const INFO = {
.table-cell {
padding: 10px 5px;
border: 1px solid #ddd;
text-align: center;
font-size: 12px;
text-align: center;
}
.text-cell {
word-break: break-all;
@@ -1,33 +1,382 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style>
#app {
--color-primary: #1989fa;
}
.difficult-help-list-page {
min-height: 100vh;
padding-bottom: calc(24px + env(safe-area-inset-bottom));
box-sizing: border-box;
background: #f3f7fd;
}
.difficult-help-list-page .van-nav-bar,
.difficult-help-list-page .van-sticky--fixed {
background: #fff;
}
.difficult-help-list-page .van-nav-bar__title {
color: #172033;
font-size: 16px;
font-weight: 600;
}
.difficult-help-list-page .van-nav-bar .van-icon,
.difficult-help-list-page .van-nav-bar__text {
color: #1989fa;
}
.difficult-help-list-sticky {
padding: 22px 12px 1px;
box-sizing: border-box;
background: #f3f7fd;
}
.difficult-help-list-search {
margin: 0 0 8px;
padding: 0;
overflow: hidden;
border-radius: 12px;
background: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .1);
}
.difficult-help-list-search .van-search__content {
height: 42px;
padding-left: 12px;
align-items: center;
border-radius: 12px;
background: #fff;
}
.difficult-help-list-search .van-field__control {
color: #263548;
font-size: 14px;
}
.difficult-help-list-search .van-field__control::placeholder {
color: #a7b0bf;
}
.difficult-help-list-filter {
margin-bottom: 10px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
transition: border-radius .18s ease;
}
.difficult-help-list-filter.is-open {
border-radius: 10px 10px 0 0;
}
.difficult-help-list-filter .van-dropdown-menu__bar {
height: 44px;
box-shadow: none;
}
.difficult-help-list-filter .van-dropdown-menu__item,
.difficult-help-list-filter .van-dropdown-menu__title {
height: 44px;
}
.difficult-help-list-filter .van-dropdown-menu__item {
align-items: center;
justify-content: center;
}
.difficult-help-list-filter .van-dropdown-menu__title {
display: inline-flex;
align-items: center;
line-height: 20px;
color: #66758a;
font-size: 14px;
}
.difficult-help-list-filter .van-dropdown-menu__title--active,
.difficult-help-list-filter .van-dropdown-item__option--active,
.difficult-help-list-filter .van-dropdown-item__option--active .van-cell__title,
.difficult-help-list-filter .van-dropdown-item__option--active .van-dropdown-item__icon {
color: #1989fa;
}
.difficult-help-list-filter .van-dropdown-item__content {
width: calc(100% - 24px);
max-height: 50%;
margin: 0 12px;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
box-sizing: border-box;
border-radius: 0 0 12px 12px;
box-shadow: 0 10px 24px rgba(31, 65, 108, .14);
}
.difficult-help-list-filter .van-dropdown-item__option {
display: flex;
min-height: 48px;
padding: 0 16px;
align-items: center;
color: #46566c;
font-size: 14px;
}
.difficult-help-list-filter .van-dropdown-item__option .van-cell__title {
display: flex;
min-width: 0;
min-height: 20px;
align-items: center;
flex: 1 1 auto;
line-height: 20px;
}
.difficult-help-list-filter .van-dropdown-item__option .van-cell__value {
display: flex;
width: 20px;
min-width: 0;
overflow: visible;
align-items: center;
justify-content: flex-end;
flex: 0 0 20px;
}
.difficult-help-list-filter .van-dropdown-item__option .van-dropdown-item__icon {
margin-left: 4px;
}
.difficult-help-list-content {
padding: 0 12px;
}
.difficult-help-list-skeleton {
padding-bottom: 2px;
}
.difficult-help-list-skeleton__card {
margin-bottom: 12px;
padding: 14px;
overflow: hidden;
box-sizing: border-box;
border: 1px solid #e9eff7;
border-radius: 12px;
background: #fff;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
}
.difficult-help-list-skeleton__card .van-skeleton {
padding: 0;
}
.difficult-help-list-skeleton__card .van-skeleton__title {
width: 34%;
height: 17px;
margin-bottom: 13px;
}
.difficult-help-list-skeleton__card .van-skeleton__row {
height: 12px;
margin-top: 11px;
}
.difficult-help-list-skeleton__card .van-skeleton__row:nth-child(2n) {
width: 88%;
}
.difficult-help-list-skeleton__card .van-skeleton__row:last-child {
width: 48%;
margin-top: 18px;
}
.difficult-help-list-page .table-list-container {
margin-top: 0;
padding-bottom: 2px;
}
.difficult-help-list-page .table-list-container .table-list-item {
margin-bottom: 12px;
padding: 14px;
border: 1px solid #e9eff7;
border-radius: 12px;
box-shadow: 0 5px 16px rgba(43, 73, 112, .08);
}
.difficult-help-card-header {
display: flex;
margin-bottom: 8px;
align-items: flex-start;
justify-content: space-between;
}
.difficult-help-card-heading {
min-width: 0;
padding-right: 10px;
flex: 1;
}
.difficult-help-card-title {
overflow: hidden;
color: #253247;
font-size: 15px;
font-weight: 600;
line-height: 22px;
text-overflow: ellipsis;
white-space: nowrap;
}
.difficult-help-card-code {
margin-top: 1px;
color: #9ba6b6;
font-size: 12px;
line-height: 18px;
}
.difficult-help-card-status {
display: inline-flex;
flex: none;
}
.difficult-help-card-status .van-tag {
min-height: 24px;
padding: 3px 9px;
border: 0;
border-radius: 12px;
box-sizing: border-box;
font-size: 12px;
line-height: 18px;
}
.difficult-help-list-page .table-list-container .table-list-item .item-actions {
margin-top: 10px;
padding-top: 10px;
border-top-color: #edf1f6;
}
.difficult-help-list-page .table-list-container .table-list-item .action-btn {
min-height: 28px;
padding: 4px 11px;
border-radius: 14px;
color: #1989fa;
background: #eaf4ff;
font-size: 12px;
line-height: 18px;
}
.difficult-help-list-page .table-list-container .table-list-item .action-btn.delete {
color: #f04444;
background: #fff0f0;
}
.difficult-help-list-page .table-list-container .table-list-item .action-btn i,
.difficult-help-list-page .table-list-container .table-list-item .action-btn .van-icon {
margin-right: 5px;
font-size: 13px;
}
.difficult-help-list-page .empty-state {
padding: 0;
}
.difficult-help-list-empty {
display: flex;
min-height: 280px;
padding: 46px 18px 36px;
align-items: center;
justify-content: center;
box-sizing: border-box;
flex-direction: column;
text-align: center;
}
.difficult-help-list-empty img {
display: block;
width: 86%;
max-width: 290px;
height: auto;
object-fit: contain;
}
.difficult-help-list-empty__title {
margin-top: 14px;
color: #50627a;
font-size: 15px;
font-weight: 600;
line-height: 22px;
}
.difficult-help-list-empty__hint {
margin-top: 5px;
color: #9aa7b8;
font-size: 12px;
line-height: 18px;
}
</style>
<style id="style-difficult-help-info-h5">
<!--#include("../common/info.css"){}#-->
</style>
<div id="app">
<van-sticky>
<div class="difficult-help-list-page">
<van-nav-bar title="帮扶申请记录" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<div class="difficult-help-list-sticky">
<van-search
v-model="pageForm.searchKeyword"
class="difficult-help-list-search"
placeholder="请输入工号或者姓名进行查询"
show-action
:reverse-color="false"
input-align="left"
@search="doSearch">
<template #action>
<div @click="doSearch">搜索</div>
</template>
shape="round"
clearable
@search="doSearch"
@clear="doSearch">
</van-search>
<van-dropdown-menu
class="difficult-help-list-filter"
:class="{'is-open': filterOpened}"
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
active-color="#1989fa"
:close-on-click-outside="false"
:close-on-click-overlay="false"
>
<van-dropdown-item :options="unionList" @change="unionChange" v-model="pageForm.unionId"></van-dropdown-item>
<van-dropdown-item :options="unitList" @change="doSearch" v-model="pageForm.unitId"></van-dropdown-item>
<van-dropdown-item
:options="unionList"
@change="unionChange"
@open="filterOpened = true"
@close="filterOpened = false"
v-model="pageForm.unionId">
</van-dropdown-item>
<van-dropdown-item
:options="unitList"
@change="doSearch"
@open="filterOpened = true"
@close="filterOpened = false"
v-model="pageForm.unitId">
</van-dropdown-item>
</van-dropdown-menu>
</div>
</van-sticky>
<table-list api="/platform/difficultHelp/mine/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName" @ready="doSearch">
<main class="difficult-help-list-content">
<div class="difficult-help-list-skeleton" v-if="skeletonLoading">
<div class="difficult-help-list-skeleton__card" v-for="index in 3" :key="'mine-skeleton-' + index">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<table-list api="/platform/difficultHelp/mine/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName"
v-show="!skeletonLoading" :show_loading_overlay="false" @ready="doSearch"
@loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="difficult-help-card-header">
<div class="difficult-help-card-heading">
<div class="difficult-help-card-title">{{row.userName || '--'}}</div>
<div class="difficult-help-card-code">工号:{{row.loginName || '--'}}</div>
</div>
<enum-tag class="difficult-help-card-status" v-if="row.instanceState" :value="row.instanceState"
name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
</div>
</template>
<template v-slot="{index,row}">
<table-column label="受助人">{{row.userName + '(' + row.loginName + ')'}}</table-column>
<table-column label="申请人">{{row.proxyUserName || '--'}}</table-column>
<table-column label="所属工会">{{row.unionName}}</table-column>
<table-column label="工作单位">{{row.unitName}}</table-column>
<table-column label="困难类型">{{row.subsidyStandards}}</table-column>
@@ -36,27 +385,36 @@ layout("/layouts/platform_h5.html"){
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<van-icon name="eye-o"></van-icon>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskKey === 'startTask' || !row.instanceId"
@click="onEdit(row)">
<i class="fa fa-edit"></i>
<van-icon name="edit"></van-icon>
<span>编辑</span>
</div>
<div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">
<i class="fa fa-undo"></i>
<van-icon name="revoke"></van-icon>
<span>撤回</span>
</div>
<div class="action-btn delete" v-if="row.taskKey === 'startTask' || !row.instanceId"
@click="onDelete(row.id)">
<i class="fa fa-trash"></i>
<van-icon name="delete-o"></van-icon>
<span>删除</span>
</div>
</template>
<template #empty>
<div class="difficult-help-list-empty">
<img src="/assets/mobile/img/difficultHelp/difficult-help-empty.png" alt="暂无帮扶申请记录插画">
<div class="difficult-help-list-empty__title">暂无帮扶申请记录</div>
<div class="difficult-help-list-empty__hint">当前筛选条件下暂时没有帮扶申请</div>
</div>
</template>
</table-list>
</main>
<info ref="infoRef"></info>
<info ref="infoRef" page-key="difficult-help-mine-detail" full-screen></info>
</div>
</div>
<script nonce="${cspNonce!}">
<!--#include("../common/info.js"){}#-->
@@ -75,6 +433,8 @@ layout("/layouts/platform_h5.html"){
},
unionList: [],
unitList: [],
filterOpened: false,
skeletonLoading: true,
}
},
components: {
@@ -83,6 +443,11 @@ layout("/layouts/platform_h5.html"){
methods: {
historyBack,
// state.initial 表示当前查询前列表为空,仅此阶段使用整组卡片骨架屏。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, "skeletonLoading", state.loading)
},
onView(row) {
this.$refs.infoRef.onOpen(row)
},
@@ -124,49 +489,51 @@ layout("/layouts/platform_h5.html"){
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
// 等待下拉框的 v-model 与 table-list 内部分页参数同步后再查询,避免旧参数回写覆盖新选中值。
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
})
},
unionChange() {
this.$set(this.pageForm, "unitId", null)
this.flushUnits()
this.doSearch()
},
async initUnion() {
// unionId 为当前用户可查询的工会范围;返回 Promise,结果写入 Vant 工会选项。
initUnion() {
const hasAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
const unionId = hasAdmin ? null : this.$store.state.user.union.id
this.unionList = await this.$businessTool.listUnion(unionId)
this.unionList.forEach((v) => {
v.text = v.name
v.value = v.id
})
if (hasAdmin) {
this.unionList.unshift({ text: "全部工会", value: null })
}
if (this.unionList && this.unionList.length > 0) {
this.$set(this.pageForm, "unionId", this.unionList[0].value)
}
return this.$axios.post("/open/common/unionOptions", {unionId}, {h5SkeletonLoading: true})
.then((res) => {
this.$set(this, "unionList", res && res.code === 0 ? (res.data || []) : [])
this.unionList.forEach((v) => {
this.$set(v, "text", v.name)
this.$set(v, "value", v.id)
})
if (hasAdmin) this.unionList.unshift({text: "全部工会", value: null})
if (this.unionList.length > 0) this.$set(this.pageForm, "unionId", this.unionList[0].value)
})
},
async flushUnits() {
// unionId 为已选工会;返回 Promise,结果写入 Vant 单位选项。
flushUnits() {
const hasAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
const unionId = hasAdmin ? this.pageForm.unionId : this.$store.state.user.union.id
this.unitList = await this.$businessTool.listUnit(unionId)
this.unitList.forEach((v) => {
v.text = v.name
v.value = v.id
})
if (hasAdmin && !this.pageForm.unionId) {
this.unitList.unshift({ text: "全部单位", value: null })
}
if (this.unitList && this.unitList.length > 0) {
this.$set(this.pageForm, "unitId", this.unitList[0].value)
}
return this.$axios.post("/open/common/unitOptions", {unionId}, {h5SkeletonLoading: true})
.then((res) => {
this.$set(this, "unitList", res && res.code === 0 ? (res.data || []) : [])
this.unitList.forEach((v) => {
this.$set(v, "text", v.name)
this.$set(v, "value", v.id)
})
if (hasAdmin && !this.pageForm.unionId) this.unitList.unshift({text: "全部单位", value: null})
if (this.unitList.length > 0) this.$set(this.pageForm, "unitId", this.unitList[0].value)
})
},
},
async created() {
await this.initUnion()
await this.flushUnits()
created() {
this.initUnion().then(() => this.flushUnits())
}
})
</script>
@@ -1,38 +1,81 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style id="style-difficult-help-approval-list-h5">
<!--#include("../common/approval-list.css"){}#-->
</style>
<style id="style-difficult-help-info-h5">
<!--#include("../common/info.css"){}#-->
</style>
<div id="app">
<van-sticky>
<div class="difficult-help-approval-page">
<van-nav-bar title="帮扶校工会审核" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<div class="difficult-help-approval-sticky">
<van-search
v-model="pageForm.searchKeyword"
class="difficult-help-approval-search"
placeholder="请输入工号或者姓名进行查询"
show-action
:reverse-color="false"
input-align="left"
@search="doSearch">
<template #action>
<div @click="doSearch">搜索</div>
</template>
shape="round"
clearable
@search="doSearch"
@clear="doSearch">
</van-search>
<van-dropdown-menu
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
class="difficult-help-approval-filter"
:class="{'is-open': filterOpened}"
active-color="#1989fa"
:close-on-click-outside="false"
:close-on-click-overlay="false"
>
<van-dropdown-item :options="unionList" @change="unionChange" v-model="pageForm.unionId"></van-dropdown-item>
<van-dropdown-item :options="unitList" @change="doSearch" v-model="pageForm.unitId"></van-dropdown-item>
<van-dropdown-item
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
:options="unionList"
@change="unionChange"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.unionId">
</van-dropdown-item>
<van-dropdown-item
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
:options="unitList"
@change="doSearch"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.unitId">
</van-dropdown-item>
<van-dropdown-item
:options="approvalOptions"
@change="onApprovalFilterChange"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.approvalText">
</van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.approvalText"
@change="(val)=>{this.pageForm.audit = val==='1';this.doSearch();}">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</div>
</van-sticky>
<table-list api="/platform/difficultHelp/schoolUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName" @ready="onReady">
<main class="difficult-help-approval-content">
<div class="difficult-help-list-skeleton" v-if="skeletonLoading">
<div class="difficult-help-list-skeleton__card" v-for="index in 3" :key="'school-approval-skeleton-' + index">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<table-list api="/platform/difficultHelp/schoolUnionApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName"
v-show="!skeletonLoading" :show_loading_overlay="false" @ready="onReady"
@loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="difficult-help-approval-card-header">
<div class="difficult-help-approval-card-heading">
<div class="difficult-help-approval-card-title">{{row.userName || '--'}}</div>
<div class="difficult-help-approval-card-code">工号:{{row.loginName || '--'}}</div>
</div>
<enum-tag class="difficult-help-approval-card-status" v-if="row.instanceState" :value="row.instanceState"
name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
</div>
</template>
<template v-slot="{index,row}">
<table-column label="受助人">{{row.userName + '(' + row.loginName + ')'}}</table-column>
<table-column label="申请人">{{row.proxyUserName || '--'}}</table-column>
<table-column label="所属工会">{{row.unionName}}</table-column>
<table-column label="工作单位">{{row.unitName}}</table-column>
<table-column label="困难类型">{{row.subsidyStandards}}</table-column>
@@ -41,72 +84,101 @@ layout("/layouts/platform_h5.html"){
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<van-icon name="eye-o"></van-icon>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
<i class="fa fa-check"></i>
<van-icon name="edit"></van-icon>
<span>审核</span>
</div>
<div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">
<i class="fa fa-undo"></i>
<van-icon name="revoke"></van-icon>
<span>撤回</span>
</div>
</template>
<template #empty>
<div class="difficult-help-approval-empty">
<img src="/assets/mobile/img/difficultHelp/difficult-help-empty.png" alt="暂无帮扶审核记录插画">
<div class="difficult-help-approval-empty__title">暂无帮扶审核记录</div>
<div class="difficult-help-approval-empty__hint">当前筛选条件下暂时没有帮扶申请</div>
</div>
</template>
</table-list>
</main>
<info ref="infoRef">
<info ref="infoRef" page-key="difficult-help-school-union-detail" full-screen>
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<van-form ref="formRef">
<van-field
v-model="formData.level"
name="level"
label="级别"
placeholder="请选择级别"
is-link
@click="showLevelPicker = true"
:rules="[{ required: true, message: '请选择级别' }]"
required
></van-field>
<van-popup v-model="showLevelPicker" position="bottom">
<van-picker
show-toolbar
:columns="levelColumns"
@confirm="onLevelConfirm"
@cancel="showLevelPicker=false">
</van-picker>
</van-popup>
<van-field
v-model="formData.money"
name="money"
label="审核金额"
placeholder="请输入审核金额"
type="number"
:rules="[{ required: true, message: '请输入审核金额' }]"
required
></van-field>
<van-field
v-model="formData.tf_opinion"
name="tf_opinion"
label="审批意见"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
required
maxlength="100"
show-word-limit
></van-field>
<section class="difficult-help-info-card">
<h2 class="difficult-help-info-card__title">审核信息</h2>
<div class="difficult-help-audit-form">
<van-field
v-model="formData.level"
class="difficult-help-audit-select"
name="level"
label="级别"
placeholder="请选择级别"
readonly
is-link
required
@click="openLevelPicker"
:rules="[{ required: true, message: '请选择级别' }]"
></van-field>
<van-field
v-model="formData.money"
class="difficult-help-audit-money"
name="money"
label="审核金额"
placeholder="请输入审核金额"
type="number"
required
:rules="[{ required: true, message: '请输入审核金额' }]"
></van-field>
</div>
</section>
<section class="difficult-help-info-card">
<h2 class="difficult-help-info-card__title">
审批意见<span class="difficult-help-audit-card__required">*</span>
</h2>
<van-field
v-model="formData.tf_opinion"
class="difficult-help-audit-textarea"
name="tf_opinion"
type="textarea"
rows="6"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
maxlength="100"
show-word-limit
></van-field>
</section>
</van-form>
<div style="display: flex; justify-content: space-between; column-gap: 10px; padding: 10px">
<van-button type="primary" block @click="handleTaskAction(1)">同意</van-button>
<van-button type="danger" block @click="handleTaskAction(2)">拒绝</van-button>
<van-button type="danger" block @click="handleTaskAction(6)">退回</van-button>
<div class="difficult-help-popup-actions">
<van-button class="difficult-help-action-danger" type="danger" block :loading="formLoading" @click="handleTaskAction(6)">退回</van-button>
<van-button class="difficult-help-action-danger" type="danger" block :loading="formLoading" @click="handleTaskAction(2)">拒绝</van-button>
<van-button class="difficult-help-action-primary" type="primary" block :loading="formLoading" @click="handleTaskAction(1)">同意</van-button>
</div>
</div>
</info>
<van-popup :value="showLevelPicker" position="bottom" :close-on-click-overlay="false">
<van-picker
show-toolbar
:columns="levelColumns"
@confirm="onLevelConfirm"
@cancel="closeLevelPicker">
</van-picker>
</van-popup>
<van-dialog
:value="confirmVisible"
title="提示"
:message="confirmActionType === 'revoke' ? '您确定要撤回吗?' : '您确定要提交吗?'"
show-cancel-button
confirm-button-color="#1989fa"
@confirm="confirmPendingAction"
@cancel="closeActionConfirm">
</van-dialog>
</div>
</div>
<script nonce="${cspNonce!}">
<!--#include("../common/info.js"){}#-->
@@ -117,8 +189,9 @@ layout("/layouts/platform_h5.html"){
data() {
return {
pageForm: {
pageNo: 1,
pageNumber: 1,
pageSize: 10,
totalCount: 0,
searchKeyword: '',
unionId: '',
unitId: '',
@@ -127,12 +200,23 @@ layout("/layouts/platform_h5.html"){
},
unionList: [],
unitList: [],
approvalOptions: [
{text: "未审核", value: "0"},
{text: "已审核", value: "1"}
],
filterOpened: false,
skeletonLoading: true,
formData: {},
showApprovalForm: false,
formLoading: false,
showLevelPicker: false,
levelColumns: []
levelColumns: [],
confirmVisible: false,
confirmActionType: "task",
pendingSubmitType: null,
pendingRevokeRow: null,
historyLayerUnsubscribe: null
}
},
components: {
@@ -141,6 +225,24 @@ layout("/layouts/platform_h5.html"){
methods: {
historyBack,
// state.initial 表示当前查询前列表为空,仅此阶段使用整组卡片骨架屏。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, "skeletonLoading", state.loading)
},
// 审核状态下拉值为字符串,接口参数 audit 继续使用布尔值。
onApprovalFilterChange(val) {
this.$set(this.pageForm, "audit", val === "1")
this.doSearch()
},
// 同步筛选面板展开状态,用于调整下拉框底部圆角。
onFilterOpen() {
this.$set(this, "filterOpened", true)
},
onFilterClose() {
this.$set(this, "filterOpened", false)
},
onReady() {
this.doSearch()
},
@@ -154,123 +256,179 @@ layout("/layouts/platform_h5.html"){
},
onView(row) {
this.showApprovalForm = false
this.$refs.infoRef.onOpen(row)
this.$set(this, "showApprovalForm", false)
this.$refs.infoRef.onOpen(row, "帮扶详情")
},
onApproval(row) {
this.showApprovalForm = true
this.$refs.infoRef.onOpen(row)
this.formData = {
this.$set(this, "showApprovalForm", true)
this.$set(this, "formData", {
processTaskId: row.taskId,
taskName: row.curTaskName,
money: 0,
id: row.id,
level: "",
tf_opinion: ""
}
})
this.$refs.infoRef.onOpen(row, row.curTaskName || "帮扶审核")
},
async initLevelDict() {
const levelData = await this.$businessTool.getDictOptions("DIFFICULT_LEVEL");
this.levelColumns = levelData.map(item => {
return { text: item.name, value: item.code };
});
// 加载困难级别字典并转换为 Vant Picker 所需的 text/value 结构。
initLevelDict() {
return this.$axios.post("/open/common/dictOptions", {code: "DIFFICULT_LEVEL"}, {h5SkeletonLoading: true}).then((res) => {
const levelData = res && res.code === 0 ? res.data : []
this.$set(this, "levelColumns", (levelData || []).map((item) => ({
text: item.name,
value: item.code
})))
})
},
// 级别选择器作为详情上层弹框,打开时同步增加浏览器历史。
openLevelPicker() {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-level-picker")) return
this.$set(this, "showLevelPicker", true)
},
closeLevelPicker(afterClose = null) {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.close("difficult-help-level-picker", afterClose)) return
this.$set(this, "showLevelPicker", false)
if (typeof afterClose === "function") afterClose()
},
onLevelConfirm(option) {
this.formData.level = option.value;
this.showLevelPicker = false;
this.closeLevelPicker(() => {
this.$set(this.formData, "level", option.value)
})
},
async handleTaskAction(val) {
try {
await this.$refs.formRef.validate();
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
}).then(() => {
this.$axios.post("/platform/difficultHelp/schoolUnionApproval/save", {
// val 为流程提交类型:1 同意、2 拒绝、6 退回;校验通过后打开历史栈确认框。
handleTaskAction(val) {
this.$refs.formRef.validate()
.then(() => {
this.$set(this, "confirmActionType", "task")
this.$set(this, "pendingSubmitType", val)
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-action-confirm")) return
this.$set(this, "confirmVisible", true)
})
.catch(() => {})
},
// 确认操作时先回退确认框历史,再根据操作类型调用办理或撤回接口。
confirmPendingAction() {
this.closeActionConfirm(() => {
if (this.confirmActionType === "revoke") {
this.revokeTask()
return
}
this.submitTaskAction()
})
},
// 先保存级别和金额,再提交 processTaskId、审批意见及流程提交类型。
submitTaskAction() {
this.$set(this, "formLoading", true)
this.$axios.post("/platform/difficultHelp/schoolUnionApproval/save", {
data: JSON.stringify({...this.formData})
})
.then((saveRes) => {
if (!saveRes || saveRes.code !== 0) return null
return this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: this.pendingSubmitType
})
}).then((saveRes) => {
if (saveRes.code === 0) {
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val
})
}).then((res) => {
if (res.code === 0) {
this.$refs.infoRef.onClose()
this.$toast.success(res.msg)
this.doSearch()
}
})
}
})
}).catch(() => {
})
} catch (error) {
}
},
// 撤回
onRevoke(row) {
debugger
this.$dialog.confirm({
title: '提示',
message: '您确定要撤回吗?',
}).then(() => {
this.$axios.post('/flow/common/revokeTask', {taskId: row.taskId}).then(res => {
if (res.code === 0) {
this.$toast.success('撤回成功');
this.doSearch();
.then((res) => {
if (res && res.code === 0) {
const afterClose = () => {
this.$toast.success(res.msg)
this.doSearch()
}
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.closeAll(afterClose)) return
this.$refs.infoRef.onClose(afterClose)
}
})
}).catch(() => {
});
.finally(() => {
this.$set(this, "formLoading", false)
})
},
closeActionConfirm(afterClose = null) {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.close("difficult-help-action-confirm", afterClose)) return
this.$set(this, "confirmVisible", false)
if (typeof afterClose === "function") afterClose()
},
// row 提供待撤回任务 taskId,确认后由统一确认框执行撤回。
onRevoke(row) {
this.$set(this, "confirmActionType", "revoke")
this.$set(this, "pendingRevokeRow", row)
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-action-confirm")) return
this.$set(this, "confirmVisible", true)
},
// 使用 pendingRevokeRow.taskId 撤回任务,接口返回统一响应对象。
revokeTask() {
if (!this.pendingRevokeRow) return
this.$set(this, "formLoading", true)
this.$axios.post("/flow/common/revokeTask", {taskId: this.pendingRevokeRow.taskId})
.then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg || "撤回成功")
this.doSearch()
}
})
.finally(() => {
this.$set(this, "formLoading", false)
})
},
unionChange() {
this.$set(this.pageForm, "unitId", null)
this.flushUnits()
this.doSearch()
},
async initUnion() {
initUnion() {
const hasAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
const unionId = hasAdmin ? null : this.$store.state.user.union.id
this.unionList = await this.$businessTool.listUnion(unionId)
this.unionList.forEach((v) => {
v.text = v.name
v.value = v.id
return this.$axios.post("/open/common/unionOptions", {unionId}, {h5SkeletonLoading: true}).then((res) => {
const list = res && res.code === 0 ? res.data : []
this.$set(this, "unionList", list || [])
this.unionList.forEach((v) => {
this.$set(v, "text", v.name)
this.$set(v, "value", v.id)
})
if (hasAdmin) this.unionList.unshift({text: "全部工会", value: null})
if (this.unionList.length > 0) this.$set(this.pageForm, "unionId", this.unionList[0].value)
})
if (hasAdmin) {
this.unionList.unshift({ text: "全部工会", value: null })
}
if (this.unionList && this.unionList.length > 0) {
this.$set(this.pageForm, "unionId", this.unionList[0].value)
}
},
async flushUnits() {
flushUnits() {
const hasAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
const unionId = hasAdmin ? this.pageForm.unionId : this.$store.state.user.union.id
this.unitList = await this.$businessTool.listUnit(unionId)
this.unitList.forEach((v) => {
v.text = v.name
v.value = v.id
return this.$axios.post("/open/common/unitOptions", {unionId}, {h5SkeletonLoading: true}).then((res) => {
const list = res && res.code === 0 ? res.data : []
this.$set(this, "unitList", list || [])
this.unitList.forEach((v) => {
this.$set(v, "text", v.name)
this.$set(v, "value", v.id)
})
this.unitList.unshift({text: "全部单位", value: null})
if (this.unitList.length > 0) this.$set(this.pageForm, "unitId", this.unitList[0].value)
})
this.unitList.unshift({ text: "全部单位", value: null })
if (this.unitList && this.unitList.length > 0) {
this.$set(this.pageForm, "unitId", this.unitList[0].value)
}
},
},
async created() {
await this.initUnion()
await this.flushUnits()
await this.initLevelDict()
created() {
this.initUnion().then(() => this.flushUnits())
this.initLevelDict()
if (!window.h5HistoryLayerManager) return
this.$set(this, "historyLayerUnsubscribe", window.h5HistoryLayerManager.subscribe((stack) => {
this.$set(this, "confirmVisible", stack.includes("difficult-help-action-confirm"))
this.$set(this, "showLevelPicker", stack.includes("difficult-help-level-picker"))
}))
},
beforeDestroy() {
if (typeof this.historyLayerUnsubscribe === "function") this.historyLayerUnsubscribe()
}
})
</script>
@@ -1,38 +1,81 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style id="style-difficult-help-approval-list-h5">
<!--#include("../common/approval-list.css"){}#-->
</style>
<style id="style-difficult-help-info-h5">
<!--#include("../common/info.css"){}#-->
</style>
<div id="app">
<van-sticky>
<div class="difficult-help-approval-page">
<van-nav-bar title="帮扶二级党组织审核" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<div class="difficult-help-approval-sticky">
<van-search
v-model="pageForm.searchKeyword"
class="difficult-help-approval-search"
placeholder="请输入工号或者姓名进行查询"
show-action
:reverse-color="false"
input-align="left"
@search="doSearch">
<template #action>
<div @click="doSearch">搜索</div>
</template>
shape="round"
clearable
@search="doSearch"
@clear="doSearch">
</van-search>
<van-dropdown-menu
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
class="difficult-help-approval-filter"
:class="{'is-open': filterOpened}"
active-color="#1989fa"
:close-on-click-outside="false"
:close-on-click-overlay="false"
>
<van-dropdown-item :options="unionList" @change="unionChange" v-model="pageForm.unionId"></van-dropdown-item>
<van-dropdown-item :options="unitList" @change="doSearch" v-model="pageForm.unitId"></van-dropdown-item>
<van-dropdown-item
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
:options="unionList"
@change="unionChange"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.unionId">
</van-dropdown-item>
<van-dropdown-item
v-if="$auth.hasRoleOr('SYSADMIN,SCHOOL_UNION_ADMIN,BRANCH_UNION_CHAIRMAN')"
:options="unitList"
@change="doSearch"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.unitId">
</van-dropdown-item>
<van-dropdown-item
:options="approvalOptions"
@change="onApprovalFilterChange"
@open="onFilterOpen"
@close="onFilterClose"
v-model="pageForm.approvalText">
</van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.approvalText"
@change="(val)=>{this.pageForm.audit = val==='1';this.doSearch();}">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</div>
</van-sticky>
<table-list api="/platform/difficultHelp/unionLeaderApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName" @ready="onReady">
<main class="difficult-help-approval-content">
<div class="difficult-help-list-skeleton" v-if="skeletonLoading">
<div class="difficult-help-list-skeleton__card" v-for="index in 3" :key="'leader-approval-skeleton-' + index">
<van-skeleton title :row="5" animated></van-skeleton>
</div>
</div>
<table-list api="/platform/difficultHelp/unionLeaderApproval/pageData" :page_form.sync="pageForm" ref="tableListRef" title="userName"
v-show="!skeletonLoading" :show_loading_overlay="false" @ready="onReady"
@loading-change="handleListLoadingChange">
<template #header="{index,row}">
<div class="difficult-help-approval-card-header">
<div class="difficult-help-approval-card-heading">
<div class="difficult-help-approval-card-title">{{row.userName || '--'}}</div>
<div class="difficult-help-approval-card-code">工号:{{row.loginName || '--'}}</div>
</div>
<enum-tag class="difficult-help-approval-card-status" v-if="row.instanceState" :value="row.instanceState"
name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
</div>
</template>
<template v-slot="{index,row}">
<table-column label="受助人">{{row.userName + '(' + row.loginName + ')'}}</table-column>
<table-column label="申请人">{{row.proxyUserName || '--'}}</table-column>
<table-column label="所属工会">{{row.unionName}}</table-column>
<table-column label="工作单位">{{row.unitName}}</table-column>
<table-column label="困难类型">{{row.subsidyStandards}}</table-column>
@@ -41,54 +84,101 @@ layout("/layouts/platform_h5.html"){
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<van-icon name="eye-o"></van-icon>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onApproval(row)">
<i class="fa fa-check"></i>
<van-icon name="edit"></van-icon>
<span>审核</span>
</div>
<div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)">
<i class="fa fa-undo"></i>
<van-icon name="revoke"></van-icon>
<span>撤回</span>
</div>
</template>
<template #empty>
<div class="difficult-help-approval-empty">
<img src="/assets/mobile/img/difficultHelp/difficult-help-empty.png" alt="暂无帮扶审核记录插画">
<div class="difficult-help-approval-empty__title">暂无帮扶审核记录</div>
<div class="difficult-help-approval-empty__hint">当前筛选条件下暂时没有帮扶申请</div>
</div>
</template>
</table-list>
</main>
<info ref="infoRef">
<info ref="infoRef" page-key="difficult-help-union-leader-detail" full-screen>
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<van-form ref="formRef">
<el-form-item label="级别" prop="level" :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<search-item>
<dict-select v-model="formData.level" code="DIFFICULT_LEVEL" style="width: 100%" placeholder="请选择级别"></dict-select>
</search-item>
</el-form-item>
<el-form-item label="审核金额" prop="money" :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<el-input-number placeholder="请输入金额" v-model="formData.money" style="width: 100%" :precision="2"
:min="0" ></el-input-number>
</el-form-item>
<van-field
v-model="formData.tf_opinion"
name="tf_opinion"
label="审批意见"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
required
maxlength="100"
show-word-limit
></van-field>
<section class="difficult-help-info-card">
<h2 class="difficult-help-info-card__title">审核信息</h2>
<div class="difficult-help-audit-form">
<van-field
v-model="formData.level"
class="difficult-help-audit-select"
name="level"
label="级别"
placeholder="请选择级别"
readonly
is-link
required
@click="openLevelPicker"
:rules="[{ required: true, message: '请选择级别' }]"
></van-field>
<van-field
v-model="formData.money"
class="difficult-help-audit-money"
name="money"
label="审核金额"
placeholder="请输入审核金额"
type="number"
required
:rules="[{ required: true, message: '请输入审核金额' }]"
></van-field>
</div>
</section>
<section class="difficult-help-info-card">
<h2 class="difficult-help-info-card__title">
审批意见<span class="difficult-help-audit-card__required">*</span>
</h2>
<van-field
v-model="formData.tf_opinion"
class="difficult-help-audit-textarea"
name="tf_opinion"
type="textarea"
rows="6"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
maxlength="100"
show-word-limit
></van-field>
</section>
</van-form>
<div style="display: flex; justify-content: space-between; column-gap: 10px; padding: 10px">
<van-button type="primary" block @click="handleTaskAction(1)">同意</van-button>
<van-button type="danger" block @click="handleTaskAction(2)">拒绝</van-button>
<van-button type="danger" block @click="handleTaskAction(6)">退回</van-button>
<div class="difficult-help-popup-actions">
<van-button class="difficult-help-action-danger" type="danger" block :loading="formLoading" @click="handleTaskAction(6)">退回</van-button>
<van-button class="difficult-help-action-danger" type="danger" block :loading="formLoading" @click="handleTaskAction(2)">拒绝</van-button>
<van-button class="difficult-help-action-primary" type="primary" block :loading="formLoading" @click="handleTaskAction(1)">同意</van-button>
</div>
</div>
</info>
<van-popup :value="showLevelPicker" position="bottom" :close-on-click-overlay="false">
<van-picker
show-toolbar
:columns="levelColumns"
@confirm="onLevelConfirm"
@cancel="closeLevelPicker">
</van-picker>
</van-popup>
<van-dialog
:value="confirmVisible"
title="提示"
:message="confirmActionType === 'revoke' ? '您确定要撤回吗?' : '您确定要提交吗?'"
show-cancel-button
confirm-button-color="#1989fa"
@confirm="confirmPendingAction"
@cancel="closeActionConfirm">
</van-dialog>
</div>
</div>
<script nonce="${cspNonce!}">
<!--#include("../common/info.js"){}#-->
@@ -99,8 +189,9 @@ layout("/layouts/platform_h5.html"){
data() {
return {
pageForm: {
pageNo: 1,
pageNumber: 1,
pageSize: 10,
totalCount: 0,
searchKeyword: '',
unionId: '',
unitId: '',
@@ -109,9 +200,23 @@ layout("/layouts/platform_h5.html"){
},
unionList: [],
unitList: [],
approvalOptions: [
{text: "未审核", value: "0"},
{text: "已审核", value: "1"}
],
filterOpened: false,
skeletonLoading: true,
formData: {},
showApprovalForm: false
showApprovalForm: false,
formLoading: false,
showLevelPicker: false,
levelColumns: [],
confirmVisible: false,
confirmActionType: "task",
pendingSubmitType: null,
pendingRevokeRow: null,
historyLayerUnsubscribe: null
}
},
components: {
@@ -120,6 +225,24 @@ layout("/layouts/platform_h5.html"){
methods: {
historyBack,
// state.initial 表示当前查询前列表为空,仅此阶段使用整组卡片骨架屏。
handleListLoadingChange(state) {
if (state.initial) this.$set(this, "skeletonLoading", state.loading)
},
// 审核状态下拉值为字符串,接口参数 audit 继续使用布尔值。
onApprovalFilterChange(val) {
this.$set(this.pageForm, "audit", val === "1")
this.doSearch()
},
// 同步筛选面板展开状态,用于调整下拉框底部圆角。
onFilterOpen() {
this.$set(this, "filterOpened", true)
},
onFilterClose() {
this.$set(this, "filterOpened", false)
},
onReady() {
this.doSearch()
},
@@ -134,99 +257,172 @@ layout("/layouts/platform_h5.html"){
// 查看详情
onView(row) {
this.showApprovalForm = false
this.$refs.infoRef.onOpen(row)
this.$set(this, "showApprovalForm", false)
this.$refs.infoRef.onOpen(row, "帮扶详情")
},
onApproval(row) {
this.showApprovalForm = true
this.$refs.infoRef.onOpen(row)
this.formData = {
this.$set(this, "showApprovalForm", true)
this.$set(this, "formData", {
processTaskId: row.taskId,
taskName: row.curTaskName
}
taskName: row.curTaskName,
level: "",
money: 0,
tf_opinion: ""
})
this.$refs.infoRef.onOpen(row, row.curTaskName || "帮扶审核")
},
async handleTaskAction(val) {
try {
await this.$refs.formRef.validate();
this.$dialog.confirm({
title: "提示",
message: "您确定要提交吗?"
}).then(() => {
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: val
})
}).then((res) => {
if (res.code === 0) {
this.$refs.infoRef.onClose()
// 加载困难级别字典并转换为 Vant Picker 所需的 text/value 结构。
initLevelDict() {
return this.$axios.post("/open/common/dictOptions", {code: "DIFFICULT_LEVEL"}, {h5SkeletonLoading: true}).then((res) => {
const levelData = res && res.code === 0 ? res.data : []
this.$set(this, "levelColumns", (levelData || []).map((item) => ({
text: item.name,
value: item.code
})))
})
},
// 级别选择器作为详情上层弹框,打开时同步增加浏览器历史。
openLevelPicker() {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-level-picker")) return
this.$set(this, "showLevelPicker", true)
},
closeLevelPicker(afterClose = null) {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.close("difficult-help-level-picker", afterClose)) return
this.$set(this, "showLevelPicker", false)
if (typeof afterClose === "function") afterClose()
},
onLevelConfirm(option) {
this.closeLevelPicker(() => {
this.$set(this.formData, "level", option.value)
})
},
// val 为流程提交类型:1 同意、2 拒绝、6 退回;校验通过后打开历史栈确认框。
handleTaskAction(val) {
this.$refs.formRef.validate()
.then(() => {
this.$set(this, "confirmActionType", "task")
this.$set(this, "pendingSubmitType", val)
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-action-confirm")) return
this.$set(this, "confirmVisible", true)
})
.catch(() => {})
},
// 确认操作时先回退确认框历史,再根据操作类型调用办理或撤回接口。
confirmPendingAction() {
this.closeActionConfirm(() => {
if (this.confirmActionType === "revoke") {
this.revokeTask()
return
}
this.submitTaskAction()
})
},
// 提交级别、金额、审批意见和流程提交类型,接口返回统一响应对象。
submitTaskAction() {
this.$set(this, "formLoading", true)
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify({
...this.formData,
submitType: this.pendingSubmitType
})
})
.then((res) => {
if (res && res.code === 0) {
const afterClose = () => {
this.$toast.success(res.msg)
this.doSearch()
}
})
}).catch(() => {
})
} catch (error) {
}
},
// 撤回
onRevoke(row) {
debugger
this.$dialog.confirm({
title: '提示',
message: '您确定要撤回吗?',
}).then(() => {
this.$axios.post('/flow/common/revokeTask', {taskId: row.taskId}).then(res => {
if (res.code === 0) {
this.$toast.success('撤回成功');
this.doSearch();
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.closeAll(afterClose)) return
this.$refs.infoRef.onClose(afterClose)
}
})
}).catch(() => {
});
.finally(() => {
this.$set(this, "formLoading", false)
})
},
closeActionConfirm(afterClose = null) {
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.close("difficult-help-action-confirm", afterClose)) return
this.$set(this, "confirmVisible", false)
if (typeof afterClose === "function") afterClose()
},
// row 提供待撤回任务 taskId,确认后由统一确认框执行撤回。
onRevoke(row) {
this.$set(this, "confirmActionType", "revoke")
this.$set(this, "pendingRevokeRow", row)
if (window.h5HistoryLayerManager && window.h5HistoryLayerManager.open("difficult-help-action-confirm")) return
this.$set(this, "confirmVisible", true)
},
// 使用 pendingRevokeRow.taskId 撤回任务,接口返回统一响应对象。
revokeTask() {
if (!this.pendingRevokeRow) return
this.$set(this, "formLoading", true)
this.$axios.post("/flow/common/revokeTask", {taskId: this.pendingRevokeRow.taskId})
.then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg || "撤回成功")
this.doSearch()
}
})
.finally(() => {
this.$set(this, "formLoading", false)
})
},
unionChange() {
this.$set(this.pageForm, "unitId", null)
this.flushUnits()
this.doSearch()
},
async initUnion() {
initUnion() {
const hasAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
const unionId = hasAdmin ? null : this.$store.state.user.union.id
this.unionList = await this.$businessTool.listUnion(unionId)
this.unionList.forEach((v) => {
v.text = v.name
v.value = v.id
return this.$axios.post("/open/common/unionOptions", {unionId}, {h5SkeletonLoading: true}).then((res) => {
const list = res && res.code === 0 ? res.data : []
this.$set(this, "unionList", list || [])
this.unionList.forEach((v) => {
this.$set(v, "text", v.name)
this.$set(v, "value", v.id)
})
if (hasAdmin) this.unionList.unshift({text: "全部工会", value: null})
if (this.unionList.length > 0) this.$set(this.pageForm, "unionId", this.unionList[0].value)
})
if (hasAdmin) {
this.unionList.unshift({ text: "全部工会", value: null })
}
if (this.unionList && this.unionList.length > 0) {
this.$set(this.pageForm, "unionId", this.unionList[0].value)
}
},
async flushUnits() {
flushUnits() {
const hasAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
const unionId = hasAdmin ? this.pageForm.unionId : this.$store.state.user.union.id
this.unitList = await this.$businessTool.listUnit(unionId)
this.unitList.forEach((v) => {
v.text = v.name
v.value = v.id
return this.$axios.post("/open/common/unitOptions", {unionId}, {h5SkeletonLoading: true}).then((res) => {
const list = res && res.code === 0 ? res.data : []
this.$set(this, "unitList", list || [])
this.unitList.forEach((v) => {
this.$set(v, "text", v.name)
this.$set(v, "value", v.id)
})
this.unitList.unshift({text: "全部单位", value: null})
if (this.unitList.length > 0) this.$set(this.pageForm, "unitId", this.unitList[0].value)
})
this.unitList.unshift({ text: "全部单位", value: null })
if (this.unitList && this.unitList.length > 0) {
this.$set(this.pageForm, "unitId", this.unitList[0].value)
}
},
},
async created() {
await this.initUnion()
await this.flushUnits()
created() {
this.initUnion().then(() => this.flushUnits())
this.initLevelDict()
if (!window.h5HistoryLayerManager) return
this.$set(this, "historyLayerUnsubscribe", window.h5HistoryLayerManager.subscribe((stack) => {
this.$set(this, "confirmVisible", stack.includes("difficult-help-action-confirm"))
this.$set(this, "showLevelPicker", stack.includes("difficult-help-level-picker"))
}))
},
beforeDestroy() {
if (typeof this.historyLayerUnsubscribe === "function") this.historyLayerUnsubscribe()
}
})
</script>