Merge branch 'master' of http://119.29.194.155:8894/zwq/zhxy-jsd
# Conflicts: # src/pages/view/notice/publish.vue
This commit is contained in:
commit
6a30a11617
@ -83,11 +83,27 @@ export const mobilejllistApi = async (params: any) => {
|
|||||||
return res.result;
|
return res.result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getByJlIdApi = async (params: any) => {
|
||||||
|
const res = await get("/mobile/jl/getByJlId", params);
|
||||||
|
return res.result;
|
||||||
|
};
|
||||||
|
|
||||||
// 提交点名信息
|
// 提交点名信息
|
||||||
export const jsdXkdmListApi = async (params: any) => {
|
export const jsdXkdmListApi = async (params: any) => {
|
||||||
return await post("/mobile/js/xkdm/add", params);
|
return await post("/mobile/js/xkdm/add", params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 推送清单相关API
|
||||||
|
// 根据接龙ID获取学生信息
|
||||||
|
export const jlzxFindByJlParamsApi = async (params: { jlId: string }) => {
|
||||||
|
return await get("/api/jlzx/findByJlParams", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 保存推送信息
|
||||||
|
export const xxtsSaveByJlzxParamsApi = async (params: { jlId: string }) => {
|
||||||
|
return await post("/api/xxts/saveByJlzxParams", params);
|
||||||
|
};
|
||||||
|
|
||||||
// 获取待办列表
|
// 获取待办列表
|
||||||
export const dbListApi = async (params: any) => {
|
export const dbListApi = async (params: any) => {
|
||||||
return await get("/api/db/findPage", params);
|
return await get("/api/db/findPage", params);
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<view class="flex-col-center">
|
<view class="flex-col-center">
|
||||||
<view style="width: 300rpx;height: 300rpx">
|
<view v-if="showNoDataImage" style="width: 300rpx;height: 300rpx">
|
||||||
<image src="@/static/system/currency/zwjl.png" class="wh-full"/>
|
<image src="@/static/system/currency/zwjl.png" class="wh-full"/>
|
||||||
</view>
|
</view>
|
||||||
<view class="color-9">暂无数据...</view>
|
<view class="color-9">暂无数据...</view>
|
||||||
@ -29,7 +29,7 @@
|
|||||||
</z-paging>
|
</z-paging>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {reactive} from "vue";
|
import {reactive, watch, computed} from "vue";
|
||||||
import type {LayoutOptions, PagingRefInterface} from "@/components/BasicListLayout/type/useLayout";
|
import type {LayoutOptions, PagingRefInterface} from "@/components/BasicListLayout/type/useLayout";
|
||||||
|
|
||||||
const dataList = ref([])
|
const dataList = ref([])
|
||||||
@ -41,7 +41,6 @@ const config = {
|
|||||||
'loading-full-fixed': true,
|
'loading-full-fixed': true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let componentProps = ref<any>({})
|
let componentProps = ref<any>({})
|
||||||
const emits = defineEmits(['register'])
|
const emits = defineEmits(['register'])
|
||||||
let props: LayoutOptions
|
let props: LayoutOptions
|
||||||
@ -49,7 +48,7 @@ const setProps = (propValue: LayoutOptions) => {
|
|||||||
componentProps.value = propValue.componentProps ? propValue.componentProps : {}
|
componentProps.value = propValue.componentProps ? propValue.componentProps : {}
|
||||||
props = reactive(propValue)
|
props = reactive(propValue)
|
||||||
}
|
}
|
||||||
emits('register', setProps, pagingRef)
|
emits('register', setProps, pagingRef, dataList)
|
||||||
|
|
||||||
async function query(pageNo: number, pageSize: number) {
|
async function query(pageNo: number, pageSize: number) {
|
||||||
if (props.query) {
|
if (props.query) {
|
||||||
@ -68,4 +67,7 @@ function changeSearch(e: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 控制暂无数据图片显示
|
||||||
|
const showNoDataImage = computed(() => dataList.value.length === 0)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {isFunction} from "lodash";
|
import {isFunction} from "lodash";
|
||||||
import {Ref} from "vue";
|
import {Ref, ref, nextTick} from "vue";
|
||||||
import {hideLoading, showLoading} from "@/utils/uniapp";
|
import {hideLoading, showLoading} from "@/utils/uniapp";
|
||||||
import type {
|
import type {
|
||||||
LayoutCallback,
|
LayoutCallback,
|
||||||
@ -13,21 +13,46 @@ import type {
|
|||||||
export function useLayout(options: LayoutOptions): UseLayoutInterfaceReturn {
|
export function useLayout(options: LayoutOptions): UseLayoutInterfaceReturn {
|
||||||
|
|
||||||
let methods: Ref<PagingRefInterface>
|
let methods: Ref<PagingRefInterface>
|
||||||
|
let dataListRef: Ref<any[]> | undefined
|
||||||
|
|
||||||
|
// 强制设置concat为false,使用完整数据模式
|
||||||
|
if (!options.componentProps) options.componentProps = {};
|
||||||
|
options.componentProps.concat = false;
|
||||||
|
|
||||||
|
// 用于维护所有已加载数据
|
||||||
|
const allData = ref<any[]>([]);
|
||||||
|
|
||||||
async function requestApi(pageNo: number, pageSize: number) {
|
async function requestApi(pageNo: number, pageSize: number) {
|
||||||
if (isFunction(options.api)) {
|
if (isFunction(options.api)) {
|
||||||
try {
|
try {
|
||||||
const result = await options.api(Object.assign({}, {
|
const result = await options.api(Object.assign({}, {
|
||||||
rows: pageSize,
|
pageNo: pageNo,
|
||||||
page: pageNo
|
pageSize: pageSize
|
||||||
}, options.param))
|
}, options.param))
|
||||||
await nextTick()
|
await nextTick()
|
||||||
if (methods.value) {
|
if (methods.value) {
|
||||||
// @ts-ignore
|
let newList = result[options.resultKey || 'rows'] || [];
|
||||||
await methods.value.complete(result[options.resultKey || 'rows'])
|
if (pageNo === 1) {
|
||||||
|
allData.value = [...newList];
|
||||||
|
if (dataListRef) dataListRef.value = allData.value;
|
||||||
|
} else {
|
||||||
|
// 合并去重
|
||||||
|
const map = new Map();
|
||||||
|
[...allData.value, ...newList].forEach(item => {
|
||||||
|
map.set(item.id, item);
|
||||||
|
});
|
||||||
|
allData.value = Array.from(map.values());
|
||||||
|
if (dataListRef) dataListRef.value = allData.value;
|
||||||
|
}
|
||||||
|
const hasMoreData = newList.length > 0;
|
||||||
|
if (!hasMoreData) {
|
||||||
|
await methods.value.completeByNoMore(allData.value as any, true);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
await methods.value.complete(allData.value as any);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('err', err)
|
|
||||||
if (methods.value) {
|
if (methods.value) {
|
||||||
await methods.value.complete(false);
|
await methods.value.complete(false);
|
||||||
}
|
}
|
||||||
@ -39,8 +64,9 @@ export function useLayout(options: LayoutOptions): UseLayoutInterfaceReturn {
|
|||||||
options.query = requestApi
|
options.query = requestApi
|
||||||
}
|
}
|
||||||
|
|
||||||
const register = (callback: LayoutCallback, pagingRef: Ref<PagingRefInterface>) => {
|
const register = (callback: LayoutCallback, pagingRef: Ref<PagingRefInterface>, listRef?: Ref<any[]>) => {
|
||||||
methods = pagingRef
|
methods = pagingRef
|
||||||
|
dataListRef = listRef
|
||||||
callback(options)
|
callback(options)
|
||||||
}
|
}
|
||||||
return [register,
|
return [register,
|
||||||
|
|||||||
@ -16,6 +16,7 @@ interface LayoutOptions {
|
|||||||
createdReload?: boolean //组件created时立即触发reload(可解决一些情况下先看到页面再看到loading的问题),auto为true时有效。为否时将在mounted+nextTick后触发reload
|
createdReload?: boolean //组件created时立即触发reload(可解决一些情况下先看到页面再看到loading的问题),auto为true时有效。为否时将在mounted+nextTick后触发reload
|
||||||
autoCleanListWhenReload?: boolean //reload时立即自动清空原list,若立即自动清空,则在reload之后、请求回调之前页面是空白的
|
autoCleanListWhenReload?: boolean //reload时立即自动清空原list,若立即自动清空,则在reload之后、请求回调之前页面是空白的
|
||||||
fixed?: boolean //z-paging是否使用fixed布局,若使用fixed布局,则z-paging的父view无需固定高度,z-paging高度默认铺满屏幕,页面中的view请放在z-paging标签内,需要固定在顶部的view使用slot="top"包住,需要固定在底部的view使用slot="bottom"包住。
|
fixed?: boolean //z-paging是否使用fixed布局,若使用fixed布局,则z-paging的父view无需固定高度,z-paging高度默认铺满屏幕,页面中的view请放在z-paging标签内,需要固定在顶部的view使用slot="top"包住,需要固定在底部的view使用slot="bottom"包住。
|
||||||
|
concat?: boolean //自动拼接complete中传过来的数组
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -170,10 +170,10 @@ export default {
|
|||||||
parentArr = [...parents]
|
parentArr = [...parents]
|
||||||
delete parentArr.children
|
delete parentArr.children
|
||||||
parentid.push(item[this.idKey]);
|
parentid.push(item[this.idKey]);
|
||||||
parentArr.push({
|
// 保留所有原始字段,而不仅仅是 key 和 title
|
||||||
[this.idKey]: item[this.idKey],
|
const parentItem = { ...item };
|
||||||
[this.rangeKey]: item[this.rangeKey]
|
delete parentItem.children; // 移除 children 字段避免循环引用
|
||||||
})
|
parentArr.push(parentItem);
|
||||||
this._renderTreeList(item.children, rank + 1, parentid, parentArr);
|
this._renderTreeList(item.children, rank + 1, parentid, parentArr);
|
||||||
} else {
|
} else {
|
||||||
this.treeList[this.treeList.length - 1].lastRank = true;
|
this.treeList[this.treeList.length - 1].lastRank = true;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// const ip: string = "192.168.239.1:8897";
|
const ip: string = "127.0.0.1:8897";
|
||||||
const ip: string = "yufangzc.com";
|
// const ip: string = "yufangzc.com";
|
||||||
// const ip: string = "yufangzc.com";
|
// const ip: string = "yufangzc.com";
|
||||||
const fwqip: string = "yufangzc.com";
|
const fwqip: string = "yufangzc.com";
|
||||||
//打包服务器接口代理标识
|
//打包服务器接口代理标识
|
||||||
|
|||||||
@ -377,6 +377,13 @@
|
|||||||
"enablePullDownRefresh": false
|
"enablePullDownRefresh": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/view/notice/push-list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "推送清单",
|
||||||
|
"enablePullDownRefresh": false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "pages/view/notice/selectStudents",
|
"path": "pages/view/notice/selectStudents",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@ -166,8 +166,7 @@ function toHome(data: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const {afterLoginAction} = useUserStore();
|
const {afterLoginAction, setJs} = useUserStore();
|
||||||
const {setFile} = useDataStore();
|
|
||||||
|
|
||||||
const handleVerify = async () => {
|
const handleVerify = async () => {
|
||||||
if (
|
if (
|
||||||
@ -191,6 +190,21 @@ const handleVerify = async () => {
|
|||||||
if (result.result) {
|
if (result.result) {
|
||||||
afterLoginAction(result.result);
|
afterLoginAction(result.result);
|
||||||
uni.showToast({title: "验证成功", icon: "success"});
|
uni.showToast({title: "验证成功", icon: "success"});
|
||||||
|
|
||||||
|
// 立即获取教师信息并存储到jsData中
|
||||||
|
try {
|
||||||
|
const findJsByPhoneResult = await findJsByPhoneApi({
|
||||||
|
phone: formData.phone,
|
||||||
|
});
|
||||||
|
if (findJsByPhoneResult.resultCode == 1 && findJsByPhoneResult.result) {
|
||||||
|
// 将教师信息存储到jsData中
|
||||||
|
setJs(findJsByPhoneResult.result);
|
||||||
|
console.log("教师信息已存储到jsData:", findJsByPhoneResult.result);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取教师信息失败:", error);
|
||||||
|
}
|
||||||
|
|
||||||
if (getGlobal.type == 1) {
|
if (getGlobal.type == 1) {
|
||||||
toHome(getGlobal);
|
toHome(getGlobal);
|
||||||
} else {
|
} else {
|
||||||
@ -202,7 +216,7 @@ const handleVerify = async () => {
|
|||||||
if (findJsByPhoneResult.result["confirmStatus"] == "A") {
|
if (findJsByPhoneResult.result["confirmStatus"] == "A") {
|
||||||
toHome(getGlobal);
|
toHome(getGlobal);
|
||||||
} else {
|
} else {
|
||||||
setFile(findJsByPhoneResult.result);
|
setJs(findJsByPhoneResult.result);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.reLaunch({
|
uni.reLaunch({
|
||||||
url: "/pages/view/hr/teacherProfile/index",
|
url: "/pages/view/hr/teacherProfile/index",
|
||||||
@ -386,6 +400,3 @@ input::placeholder {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
function updateUserApi(arg0: { loginName: string; phone: string; code: string;
|
|
||||||
avatarUrl: string; }) { throw new Error("Function not implemented."); }
|
|
||||||
|
|||||||
@ -6,79 +6,60 @@
|
|||||||
<view v-else-if="noticeDetail" class="detail-container">
|
<view v-else-if="noticeDetail" class="detail-container">
|
||||||
<!-- 1. 主要内容卡片 -->
|
<!-- 1. 主要内容卡片 -->
|
||||||
<view class="info-card main-content-card">
|
<view class="info-card main-content-card">
|
||||||
<!-- 封面 -->
|
<view v-if="noticeDetail.jlfm" class="cover-image-container">
|
||||||
<view v-if="noticeDetail.coverImage" class="cover-image-container">
|
|
||||||
<image
|
<image
|
||||||
:src="noticeDetail.coverImage"
|
:src="imagUrl(noticeDetail.jlfm)"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
class="cover-image"
|
class="cover-image"
|
||||||
></image>
|
></image>
|
||||||
</view>
|
</view>
|
||||||
<view v-else class="cover-placeholder">
|
<text class="notice-title">{{ noticeDetail.jlmc }}</text>
|
||||||
<uni-icons type="image" size="30" color="#ccc"></uni-icons>
|
|
||||||
<text>暂无封面</text>
|
<!-- 发布人和结束时间 -->
|
||||||
|
<view class="notice-meta">
|
||||||
|
<text class="meta-item">发布人: {{ noticeDetail.jsxm }}</text>
|
||||||
|
<text class="meta-item">结束时间: {{ noticeDetail.jljstime || noticeDetail.endTime }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 标题 -->
|
|
||||||
<text class="notice-title">{{ noticeDetail.title }}</text>
|
|
||||||
|
|
||||||
<!-- 内容 -->
|
|
||||||
<view class="notice-content">
|
<view class="notice-content">
|
||||||
<text>{{ noticeDetail.content }}</text>
|
<view v-if="!descExpanded">
|
||||||
|
<text class="desc-preview">{{ descPreview }}</text>
|
||||||
|
<u-button type="text" size="mini" class="more-btn" @click="descExpanded = true">更多</u-button>
|
||||||
</view>
|
</view>
|
||||||
|
<view v-else>
|
||||||
<!-- 附件 (简单展示) -->
|
<rich-text :nodes="noticeDetail.jlms" class="desc-rich-text"></rich-text>
|
||||||
|
<u-button type="text" size="mini" class="more-btn" @click="descExpanded = false">收起</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 2. 附件卡片 -->
|
||||||
|
<view v-if="noticeDetail.jlfj" class="info-card attachment-card">
|
||||||
|
<text class="attachment-title">附件</text>
|
||||||
|
<view class="attachment-list">
|
||||||
<view
|
<view
|
||||||
v-if="
|
|
||||||
noticeDetail.attachments && noticeDetail.attachments.length > 0
|
|
||||||
"
|
|
||||||
class="attachments-section"
|
|
||||||
>
|
|
||||||
<text class="sub-title">附件:</text>
|
|
||||||
<view
|
|
||||||
v-for="(att, index) in noticeDetail.attachments"
|
|
||||||
:key="index"
|
|
||||||
class="attachment-item"
|
class="attachment-item"
|
||||||
|
@click="previewAttachment(noticeDetail.jlfj)"
|
||||||
>
|
>
|
||||||
<uni-icons type="link" size="16" color="#007aff"></uni-icons>
|
<uni-icons type="paperplane" size="16" color="#409eff"></uni-icons>
|
||||||
<text class="attachment-name">{{ att.name }}</text>
|
<text class="attachment-name">{{ getFileName(noticeDetail.jlfj) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<!-- 3. 学生完成状态卡片 -->
|
||||||
<!-- 2. 按名单填写卡片 (反馈情况) -->
|
|
||||||
<view class="info-card feedback-card">
|
<view class="info-card feedback-card">
|
||||||
<text class="feedback-title"
|
<text class="feedback-title">接龙完成情况 ({{ receivedCount }}/{{ totalStudents }})</text>
|
||||||
>反馈完成情况 ({{ receivedCount }}/{{ totalStudents }})</text
|
|
||||||
>
|
|
||||||
<view class="name-tags">
|
<view class="name-tags">
|
||||||
<view
|
<view
|
||||||
v-for="student in noticeDetail.targetStudents"
|
v-for="stu in studentList"
|
||||||
:key="student.id"
|
:key="stu.id || stu.xsId"
|
||||||
class="name-tag"
|
class="name-tag"
|
||||||
:class="{ received: student.received }"
|
:class="{ received: stu.jlwc_status === 'A' }"
|
||||||
>
|
>
|
||||||
<text>{{ student.name }}</text>
|
<text>{{ stu.xsxm || stu.name }}</text>
|
||||||
<view v-if="student.received" class="checkmark-icon">
|
<view v-if="stu.jlwc_status === 'A'" class="checkmark-icon">
|
||||||
<uni-icons
|
<uni-icons type="checkmarkempty" size="12" color="#ffffff"></uni-icons>
|
||||||
type="checkmarkempty"
|
|
||||||
size="12"
|
|
||||||
color="#ffffff"
|
|
||||||
></uni-icons>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- Modify button is usually not shown in detail view -->
|
|
||||||
<!-- <button class="modify-btn">修改</button> -->
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 4. 时间卡片 -->
|
|
||||||
<view class="info-card list-item-card">
|
|
||||||
<view class="list-item-row no-border">
|
|
||||||
<text class="list-label">结束时间</text>
|
|
||||||
<view class="list-value">
|
|
||||||
<text>{{ noticeDetail.endTime }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -93,167 +74,135 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed } from "vue";
|
import { ref, computed, watch } from "vue";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
|
import { getByJlIdApi, jlzxFindByJlParamsApi } from "@/api/base/server";
|
||||||
interface Attachment {
|
import { imagUrl } from "@/utils";
|
||||||
name: string;
|
import { BASE_IMAGE_URL } from "@/config";
|
||||||
type: string; // e.g., 'image', 'video', 'file'
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StudentFeedback {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
received: boolean;
|
|
||||||
avatar?: string; // Optional avatar
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NoticeDetail {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
content: string;
|
|
||||||
coverImage?: string;
|
|
||||||
attachments?: Attachment[];
|
|
||||||
targetClass: string;
|
|
||||||
targetStudents: StudentFeedback[]; // Array of student feedback objects
|
|
||||||
signatureStatus: string; // e.g., '不启用', '启用中'
|
|
||||||
startTime: string;
|
|
||||||
endTime: string;
|
|
||||||
// Add other fields if needed (e.g., publisher, publishTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
const noticeId = ref<string>("");
|
const noticeId = ref<string>("");
|
||||||
const noticeDetail = ref<NoticeDetail | null>(null);
|
const noticeDetail = ref<any>(null);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
|
const studentList = ref<any[]>([]);
|
||||||
|
const descExpanded = ref(false);
|
||||||
|
const descPreview = computed(() => {
|
||||||
|
if (!noticeDetail.value?.jlms) return '';
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.innerHTML = noticeDetail.value.jlms;
|
||||||
|
return div.innerText.replace(/\n/g, '').slice(0, 100) + (div.innerText.length > 100 ? '...' : '');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取文件名
|
||||||
|
const getFileName = (filePath: string) => {
|
||||||
|
if (!filePath) return '';
|
||||||
|
const parts = filePath.split('/');
|
||||||
|
return parts[parts.length - 1] || filePath;
|
||||||
|
};
|
||||||
|
|
||||||
// Computed properties for feedback status
|
// Computed properties for feedback status
|
||||||
const receivedCount = computed(() => {
|
const receivedCount = computed(() => {
|
||||||
return (
|
return studentList.value.filter((s) => s.jlwc_status === 'A').length;
|
||||||
noticeDetail.value?.targetStudents.filter((s) => s.received).length ?? 0
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const totalStudents = computed(() => {
|
|
||||||
return noticeDetail.value?.targetStudents.length ?? 0;
|
|
||||||
});
|
});
|
||||||
|
const totalStudents = computed(() => studentList.value.length);
|
||||||
|
|
||||||
onLoad((options) => {
|
onLoad(async (options) => {
|
||||||
if (options && options.id) {
|
if (options && options.id) {
|
||||||
noticeId.value = options.id;
|
noticeId.value = options.id;
|
||||||
fetchNoticeDetail();
|
isLoading.value = true;
|
||||||
|
// 1. 获取接龙详情(新接口)
|
||||||
|
try {
|
||||||
|
const detailRes = await getByJlIdApi({ jlId: noticeId.value });
|
||||||
|
noticeDetail.value = Array.isArray(detailRes) ? detailRes[0] : {};
|
||||||
|
} catch (e) {
|
||||||
|
uni.showToast({ title: "加载接龙详情失败", icon: "none" });
|
||||||
|
}
|
||||||
|
// 2. 获取学生完成状态
|
||||||
|
try {
|
||||||
|
const stuRes = await jlzxFindByJlParamsApi({ jlId: noticeId.value });
|
||||||
|
studentList.value = stuRes?.rows || stuRes?.result || [];
|
||||||
|
console.log("学生列表数据:", studentList.value);
|
||||||
|
} catch (e) {
|
||||||
|
uni.showToast({ title: "加载学生状态失败", icon: "none" });
|
||||||
|
}
|
||||||
|
isLoading.value = false;
|
||||||
|
uni.setNavigationBarTitle({ title: "接龙情况" });
|
||||||
} else {
|
} else {
|
||||||
console.error("Notice ID is missing!");
|
|
||||||
uni.showToast({ title: "加载失败,缺少通知ID", icon: "none" });
|
uni.showToast({ title: "加载失败,缺少通知ID", icon: "none" });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 模拟获取通知详情数据
|
// 附件预览函数
|
||||||
const fetchNoticeDetail = async () => {
|
const previewAttachment = (filePath: string) => {
|
||||||
console.log(`Fetching details for notice ID: ${noticeId.value}`);
|
if (!filePath) {
|
||||||
isLoading.value = true;
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
||||||
|
|
||||||
// --- Replace with actual API call using noticeId.value ---
|
|
||||||
|
|
||||||
// Example Mock Data with targetStudents
|
|
||||||
const mockDetail: NoticeDetail = {
|
|
||||||
id: noticeId.value,
|
|
||||||
title: "重要通知接龙",
|
|
||||||
content:
|
|
||||||
"由于xxx原因,现下发《关于加强建设xxxx工作的通知》\n希望大家自习阅读,认真执行。",
|
|
||||||
// coverImage: '/static/mock/notice-cover.png', // 可选封面
|
|
||||||
attachments: [
|
|
||||||
{ name: "附件1.docx", type: "file", url: "" },
|
|
||||||
{ name: "相关图片.png", type: "image", url: "" },
|
|
||||||
],
|
|
||||||
targetClass: "一年级3班",
|
|
||||||
targetStudents: [
|
|
||||||
{ id: "s111", name: "施延兴", received: true },
|
|
||||||
{ id: "s112", name: "安苒溪", received: false },
|
|
||||||
{ id: "s113", name: "罗浩晨", received: false },
|
|
||||||
{ id: "s114", name: "康萌", received: true },
|
|
||||||
{ id: "s115", name: "范文昊", received: false },
|
|
||||||
{ id: "s116", name: "丁贺祥", received: false },
|
|
||||||
{ id: "s117", name: "韦运昊", received: false },
|
|
||||||
{ id: "s118", name: "萧润丽", received: true },
|
|
||||||
{ id: "s119", name: "谢林", received: false },
|
|
||||||
{ id: "s120", name: "鲍泽远", received: true },
|
|
||||||
{ id: "s121", name: "杨俊", received: false },
|
|
||||||
{ id: "s122", name: "秦禹辰", received: false },
|
|
||||||
{ id: "s123", name: "姜杨", received: false },
|
|
||||||
{ id: "s124", name: "窦晶滢", received: true },
|
|
||||||
{ id: "s125", name: "廉佳毅", received: false },
|
|
||||||
{ id: "s126", name: "许冰枫", received: false },
|
|
||||||
{ id: "s127", name: "曹佳惠", received: true },
|
|
||||||
{ id: "s128", name: "元运昊", received: false },
|
|
||||||
{ id: "s129", name: "孔欣怡", received: false },
|
|
||||||
{ id: "s130", name: "许润平", received: true },
|
|
||||||
{ id: "s131", name: "谢汝鑫", received: true },
|
|
||||||
{ id: "s132", name: "康益帆", received: true },
|
|
||||||
{ id: "s133", name: "凤帆", received: false },
|
|
||||||
{ id: "s134", name: "吴雅晗", received: false },
|
|
||||||
{ id: "s135", name: "曹阳", received: false },
|
|
||||||
{ id: "s136", name: "萧润丽", received: true }, // Duplicate name example
|
|
||||||
{ id: "s137", name: "褚慧嘉", received: false },
|
|
||||||
{ id: "s138", name: "薛欣怡", received: true },
|
|
||||||
{ id: "s139", name: "倪雄霖", received: false },
|
|
||||||
{ id: "s140", name: "顾雪", received: true },
|
|
||||||
], // Mock received status
|
|
||||||
signatureStatus: "不启用",
|
|
||||||
startTime: "2025-03-10 12:00:00",
|
|
||||||
endTime: "2025-03-10 12:00:00",
|
|
||||||
};
|
|
||||||
|
|
||||||
noticeDetail.value = mockDetail; // Assign the mock data
|
|
||||||
isLoading.value = false;
|
|
||||||
|
|
||||||
// 设置导航栏标题
|
|
||||||
uni.setNavigationBarTitle({ title: "通知详情" });
|
|
||||||
};
|
|
||||||
|
|
||||||
// Placeholder for attachment preview function
|
|
||||||
const previewAttachment = (attachment: Attachment) => {
|
|
||||||
console.log("尝试下载/预览附件:", attachment);
|
|
||||||
|
|
||||||
if (!attachment.url) {
|
|
||||||
uni.showToast({ title: "附件链接无效", icon: "none" });
|
uni.showToast({ title: "附件链接无效", icon: "none" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 构建完整的文件URL
|
||||||
|
const baseUrl = BASE_IMAGE_URL; // 使用配置文件中的URL
|
||||||
|
const fullUrl = filePath.startsWith('http') ? filePath : baseUrl + filePath;
|
||||||
|
|
||||||
|
// 判断文件类型
|
||||||
|
const fileExtension = filePath.split('.').pop()?.toLowerCase();
|
||||||
|
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(fileExtension || '');
|
||||||
|
|
||||||
|
// 如果是图片类型,直接预览
|
||||||
|
if (isImage) {
|
||||||
|
uni.previewImage({
|
||||||
|
urls: [fullUrl],
|
||||||
|
current: fullUrl
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uni.showLoading({ title: "正在准备附件..." });
|
uni.showLoading({ title: "正在准备附件..." });
|
||||||
|
|
||||||
uni.downloadFile({
|
uni.downloadFile({
|
||||||
url: attachment.url,
|
url: fullUrl,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.statusCode === 200) {
|
if (res.statusCode === 200) {
|
||||||
const filePath = res.tempFilePath;
|
const tempFilePath = res.tempFilePath;
|
||||||
console.log("文件下载成功:", filePath);
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
// Attempt to open the downloaded file
|
// Attempt to open the downloaded file
|
||||||
uni.openDocument({
|
uni.openDocument({
|
||||||
filePath: filePath,
|
filePath: tempFilePath,
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
console.log("打开文档成功");
|
// 打开文档成功
|
||||||
},
|
},
|
||||||
fail: function (err) {
|
fail: function (err) {
|
||||||
console.error("打开文档失败:", err);
|
|
||||||
uni.showToast({ title: "无法打开该文件类型", icon: "none" });
|
uni.showToast({ title: "无法打开该文件类型", icon: "none" });
|
||||||
uni.hideLoading(); // Ensure loading is hidden on failure too
|
uni.hideLoading(); // Ensure loading is hidden on failure too
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error("下载文件失败,服务器状态码:", res.statusCode);
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({ title: "下载附件失败", icon: "none" });
|
uni.showToast({ title: "下载附件失败", icon: "none" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error("下载文件请求失败:", err);
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
uni.showToast({ title: "下载附件失败", icon: "none" });
|
uni.showToast({ title: "下载附件失败", icon: "none" });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(noticeDetail, (val) => {
|
||||||
|
// 监听数据变化
|
||||||
|
}, { immediate: true, deep: true });
|
||||||
|
|
||||||
|
// 添加学生列表数据监听器,处理状态字段兼容性
|
||||||
|
watch(studentList, (list) => {
|
||||||
|
list.forEach(stu => {
|
||||||
|
// 兼容后端返回的驼峰写法
|
||||||
|
if (stu.jlwcStatus && !stu.jlwc_status) {
|
||||||
|
stu.jlwc_status = stu.jlwcStatus;
|
||||||
|
}
|
||||||
|
// 兼容数字状态,将'1'转换为'A'
|
||||||
|
if (stu.jlwc_status === '1') {
|
||||||
|
stu.jlwc_status = 'A';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { immediate: true, deep: true });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -370,12 +319,31 @@ const previewAttachment = (attachment: Attachment) => {
|
|||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notice-meta {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
.meta-item {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.notice-content {
|
.notice-content {
|
||||||
font-size: 15px;
|
position: relative;
|
||||||
color: #555;
|
.desc-preview {
|
||||||
line-height: 1.7;
|
display: block;
|
||||||
margin-bottom: 20px;
|
font-size: 14px;
|
||||||
white-space: pre-wrap; // 保留换行符
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.desc-rich-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设计稿中的添加附件占位
|
// 设计稿中的添加附件占位
|
||||||
@ -431,6 +399,37 @@ const previewAttachment = (attachment: Attachment) => {
|
|||||||
// Optional: Add specific padding if needed
|
// Optional: Add specific padding if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 附件卡片 */
|
||||||
|
.attachment-card {
|
||||||
|
.attachment-title {
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-list {
|
||||||
|
.attachment-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #409eff;
|
||||||
|
margin-left: 8px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.feedback-title {
|
.feedback-title {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -455,8 +454,8 @@ const previewAttachment = (attachment: Attachment) => {
|
|||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
// Calculate basis for 5 items per row, accounting for 10px gap
|
// Calculate basis for 4 items per row, accounting for 10px gap
|
||||||
flex-basis: calc((100% - 40px) / 5);
|
flex-basis: calc((100% - 30px) / 4);
|
||||||
height: 30px; // Slightly taller height
|
height: 30px; // Slightly taller height
|
||||||
line-height: 20px; // Adjust line-height for vertical centering
|
line-height: 20px; // Adjust line-height for vertical centering
|
||||||
border: 1px solid #f4f4f5;
|
border: 1px solid #f4f4f5;
|
||||||
@ -499,10 +498,17 @@ const previewAttachment = (attachment: Attachment) => {
|
|||||||
// Modify button (usually hidden in detail)
|
// Modify button (usually hidden in detail)
|
||||||
.modify-btn {
|
.modify-btn {
|
||||||
// ... styles ...
|
// ... styles ...
|
||||||
flex-basis: calc((100% - 40px) / 5); // Keep consistent basis
|
flex-basis: calc((100% - 30px) / 4); // Keep consistent basis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 更多按钮样式 */
|
||||||
|
.more-btn {
|
||||||
|
font-size: 16px !important;
|
||||||
|
color: #409eff !important;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
/* 列表项样式的卡片 */
|
/* 列表项样式的卡片 */
|
||||||
.list-item-card {
|
.list-item-card {
|
||||||
padding: 5px 15px; // 减少上下 padding
|
padding: 5px 15px; // 减少上下 padding
|
||||||
|
|||||||
@ -2,9 +2,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="jl-list-page">
|
<view class="jl-list-page">
|
||||||
<!-- 列表内容 -->
|
<!-- 列表内容 -->
|
||||||
<BasicListLayout @register="register">
|
<BasicListLayout @register="register" v-model="dataList">
|
||||||
<template v-slot="{ data }">
|
<template v-slot="{ data }">
|
||||||
<view class="jl-card" @click="goToDetail(data.id)">
|
<view class="jl-card">
|
||||||
<view class="card-header">
|
<view class="card-header">
|
||||||
<text class="jl-title">{{ data.jlmc }}</text>
|
<text class="jl-title">{{ data.jlmc }}</text>
|
||||||
<text class="jl-status" :class="getStatusClass(data.jlStatus)">
|
<text class="jl-status" :class="getStatusClass(data.jlStatus)">
|
||||||
@ -21,11 +21,26 @@
|
|||||||
<rich-text class="jl-excerpt" :nodes="data.jlms"></rich-text>
|
<rich-text class="jl-excerpt" :nodes="data.jlms"></rich-text>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-footer">
|
<view class="card-footer">
|
||||||
<text class="footer-item">发布者: {{ data.jsxm }}</text>
|
<text class="footer-item">发布者: {{ data.jsxm || '未知' }}</text>
|
||||||
<text class="footer-item">{{ formatTime(data.jlFbtime) }}</text>
|
<text class="footer-item">{{ formatTime(data.jlFbtime) }}</text>
|
||||||
<text class="footer-item" v-if="data.bjmc"
|
<text class="footer-item" v-if="data.bjmc"
|
||||||
>范围: {{ data.njmc + data.bjmc }}</text
|
>范围: {{ data.njmc + data.bjmc }}</text
|
||||||
>
|
>
|
||||||
|
<view class="footer-actions">
|
||||||
|
<u-button
|
||||||
|
class="footer-btn"
|
||||||
|
type="primary"
|
||||||
|
size="mini"
|
||||||
|
@click="goToFeedback(data.id)"
|
||||||
|
>接龙情况</u-button>
|
||||||
|
<u-button
|
||||||
|
v-if="data.jlStatus === 'A'"
|
||||||
|
class="footer-btn"
|
||||||
|
type="warning"
|
||||||
|
size="mini"
|
||||||
|
@click="goToPush(data.id)"
|
||||||
|
>消息推送</u-button>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -42,6 +57,10 @@
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</BasicListLayout>
|
</BasicListLayout>
|
||||||
|
<!-- 用uniqueList渲染 -->
|
||||||
|
<template v-for="item in uniqueList" :key="item.id">
|
||||||
|
<!-- 这里可以加自定义渲染内容做二次验证 -->
|
||||||
|
</template>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -49,13 +68,14 @@
|
|||||||
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
||||||
import { mobilejllistApi } from "@/api/base/server";
|
import { mobilejllistApi } from "@/api/base/server";
|
||||||
import { imagUrl } from "@/utils";
|
import { imagUrl } from "@/utils";
|
||||||
import BasicDragButton from "@/components/BasicDragButton/DragButton.vue";
|
import { computed, watch } from "vue";
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
|
|
||||||
interface JlItem {
|
interface JlItem {
|
||||||
id: string;
|
id: string;
|
||||||
jlmc: string; // 接龙名称
|
jlmc: string; // 接龙名称
|
||||||
jlms: string; // 接龙描述
|
jlms: string; // 接龙描述
|
||||||
jlStatus: string; // 发布状态:A已发布,B暂存
|
jlStatus: string; // 发布状态:A待推送,B暂存,C已推送
|
||||||
jlFbr: string; // 发布人
|
jlFbr: string; // 发布人
|
||||||
jlFbtime: string; // 发布时间
|
jlFbtime: string; // 发布时间
|
||||||
jlfm: string; // 接龙封面
|
jlfm: string; // 接龙封面
|
||||||
@ -72,6 +92,21 @@ const [register, { reload }] = useLayout({
|
|||||||
componentProps: {},
|
componentProps: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 兜底去重逻辑
|
||||||
|
const dataList = ref<JlItem[]>([]);
|
||||||
|
const uniqueList = computed(() => {
|
||||||
|
const map = new Map();
|
||||||
|
(dataList.value || []).forEach(item => {
|
||||||
|
if (item && item.id != null) map.set(String(item.id), item);
|
||||||
|
});
|
||||||
|
return Array.from(map.values());
|
||||||
|
});
|
||||||
|
|
||||||
|
// 日志:每次dataList变化时打印
|
||||||
|
watch(dataList, (val) => {
|
||||||
|
// 监听数据变化
|
||||||
|
});
|
||||||
|
|
||||||
// 跳转到详情页
|
// 跳转到详情页
|
||||||
const goToDetail = (jlId: string) => {
|
const goToDetail = (jlId: string) => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
@ -86,6 +121,20 @@ const goToPublish = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 跳转到接龙反馈(detail)页
|
||||||
|
const goToFeedback = (jlId: string) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/view/notice/detail?id=${jlId}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到消息推送页
|
||||||
|
const goToPush = (jlId: string) => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/view/notice/push-list?jlId=${jlId}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 根据状态获取对应的 CSS 类
|
// 根据状态获取对应的 CSS 类
|
||||||
const getStatusClass = (status: string) => {
|
const getStatusClass = (status: string) => {
|
||||||
if (status === "A") return "status-published";
|
if (status === "A") return "status-published";
|
||||||
@ -95,9 +144,9 @@ const getStatusClass = (status: string) => {
|
|||||||
|
|
||||||
// 根据状态获取状态文字
|
// 根据状态获取状态文字
|
||||||
const getStatusText = (status: string) => {
|
const getStatusText = (status: string) => {
|
||||||
if (status === "A") return "已发布";
|
if (status === "A") return "待推送";
|
||||||
if (status === "B") return "暂存";
|
if (status === "B") return "暂存";
|
||||||
return "已结束";
|
return "已推送";
|
||||||
};
|
};
|
||||||
|
|
||||||
// 格式化时间
|
// 格式化时间
|
||||||
@ -109,6 +158,10 @@ const formatTime = (timeStr: string) => {
|
|||||||
"0"
|
"0"
|
||||||
)}-${String(date.getDate()).padStart(2, "0")}`;
|
)}-${String(date.getDate()).padStart(2, "0")}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
reload();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -155,7 +208,7 @@ const formatTime = (timeStr: string) => {
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
&.status-published {
|
&.status-published {
|
||||||
background-color: #19be6b; // 绿色-已发布
|
background-color: #BE193FFF; // 红色-待推送
|
||||||
}
|
}
|
||||||
|
|
||||||
&.status-draft {
|
&.status-draft {
|
||||||
@ -163,7 +216,7 @@ const formatTime = (timeStr: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.status-ended {
|
&.status-ended {
|
||||||
background-color: #999999; // 灰色-已结束
|
background-color: #19be4d; // 绿色-已推送
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,17 +245,31 @@ const formatTime = (timeStr: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.card-footer {
|
.card-footer {
|
||||||
margin-top: 10px;
|
|
||||||
padding-top: 8px;
|
|
||||||
border-top: 1px solid #f0f0f0;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #999;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap; // 允许换行
|
flex-wrap: wrap;
|
||||||
gap: 5px 15px; // 行间距 列间距
|
gap: 5px 15px;
|
||||||
|
|
||||||
.footer-item {
|
.footer-item {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.footer-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
flex-basis: 100%; /* 独占一行 */
|
||||||
|
margin-top: 6px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.footer-btn {
|
||||||
|
min-width: 60px;
|
||||||
|
width: auto;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 0 12px;
|
||||||
|
flex: none;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
:value="formData.coverImage ? imagUrl(formData.coverImage) : ''"
|
:value="formData.coverImage ? imagUrl(formData.coverImage) : ''"
|
||||||
@select="handleCoverSelected"
|
@select="handleCoverSelected"
|
||||||
@close="handleCoverClosed"
|
@close="handleCoverClosed"
|
||||||
|
ref="coverUploadRef"
|
||||||
>
|
>
|
||||||
<view class="cover-placeholder">
|
<view class="cover-placeholder">
|
||||||
<uni-icons type="image" size="40" color="#b0b0b0"></uni-icons>
|
<uni-icons type="image" size="40" color="#b0b0b0"></uni-icons>
|
||||||
@ -162,8 +163,8 @@
|
|||||||
<button class="action-btn preview-btn" @click="previewNotice">
|
<button class="action-btn preview-btn" @click="previewNotice">
|
||||||
预览
|
预览
|
||||||
</button>
|
</button>
|
||||||
<button class="action-btn publish-btn" @click="publishNotice">
|
<button class="action-btn publish-btn" @click="publishNotice" :disabled="isPublishing">
|
||||||
立即发布
|
{{ isPublishing ? '发布中...' : '立即发布' }}
|
||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -217,13 +218,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, computed } from "vue";
|
import { ref, reactive, computed, nextTick } from "vue";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad, onShow } from "@dcloudio/uni-app";
|
||||||
import CustomUpload from "/src/components/BasicUpload/CustomUpload.vue";
|
import CustomUpload from "/src/components/BasicUpload/CustomUpload.vue";
|
||||||
import BasicTree from "@/components/BasicTree/Tree.vue";
|
import BasicTree from "@/components/BasicTree/Tree.vue";
|
||||||
import { attachmentUpload } from "@/api/system/upload";
|
import { attachmentUpload } from "@/api/system/upload";
|
||||||
import { imagUrl } from "@/utils";
|
import { imagUrl } from "@/utils";
|
||||||
import { findAllNjBjTreeApi, mobilejlstudentListApi } from "@/api/base/server";
|
import { findAllNjBjTree, mobilejlstudentListApi } from "@/api/base/server";
|
||||||
|
import { post, get } from "@/utils/request";
|
||||||
|
import { useUserStore } from "@/store/modules/user";
|
||||||
|
|
||||||
interface Attachment {
|
interface Attachment {
|
||||||
name: string;
|
name: string;
|
||||||
@ -242,6 +245,7 @@ interface FormData {
|
|||||||
targetNames: string[];
|
targetNames: string[];
|
||||||
targetStudentIds: string[];
|
targetStudentIds: string[];
|
||||||
targetNjIds: string[]; // 年级ID数组
|
targetNjIds: string[]; // 年级ID数组
|
||||||
|
targetNjmcIds: string[]; // 年级名称ID数组
|
||||||
targetBjIds: string[]; // 班级ID数组
|
targetBjIds: string[]; // 班级ID数组
|
||||||
signatureRequired: boolean;
|
signatureRequired: boolean;
|
||||||
startTime: string;
|
startTime: string;
|
||||||
@ -259,13 +263,14 @@ const formData = reactive<FormData>({
|
|||||||
targetNames: [],
|
targetNames: [],
|
||||||
targetStudentIds: [],
|
targetStudentIds: [],
|
||||||
targetNjIds: [],
|
targetNjIds: [],
|
||||||
|
targetNjmcIds: [],
|
||||||
targetBjIds: [],
|
targetBjIds: [],
|
||||||
signatureRequired: false,
|
signatureRequired: true,
|
||||||
startTime: "",
|
startTime: "",
|
||||||
endTime: "",
|
endTime: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const signatureOptions = ["不启用", "启用"];
|
const signatureOptions = ["启用" , "不启用"];
|
||||||
const signatureStatusText = computed(() => {
|
const signatureStatusText = computed(() => {
|
||||||
return formData.signatureRequired ? "启用" : "不启用";
|
return formData.signatureRequired ? "启用" : "不启用";
|
||||||
});
|
});
|
||||||
@ -283,18 +288,27 @@ const displayNames = computed(() => {
|
|||||||
return formData.targetNames.slice(0, maxDisplayCount);
|
return formData.targetNames.slice(0, maxDisplayCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 添加 ref 来引用封面上传组件
|
||||||
|
const coverUploadRef = ref();
|
||||||
|
|
||||||
// 加载树形数据
|
// 加载树形数据
|
||||||
const loadTreeData = async () => {
|
const loadTreeData = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await findAllNjBjTreeApi();
|
const res = await findAllNjBjTree();
|
||||||
if (res.resultCode === 1 && res.result) {
|
if (res.resultCode === 1 && res.result) {
|
||||||
// 转换数据格式以适配 BasicTree 组件
|
// 递归转换数据格式以适配 BasicTree 组件,同时保留 njmcId 字段
|
||||||
treeData.value = res.result.map((item: any) => ({
|
const convertTreeData = (items: any[]): any[] => {
|
||||||
|
return items.map((item: any) => ({
|
||||||
key: item.key,
|
key: item.key,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
children: item.children || [],
|
njmcId: item.njmcId, // 保留 njmcId 字段
|
||||||
|
children: item.children ? convertTreeData(item.children) : [],
|
||||||
}));
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
treeData.value = convertTreeData(res.result);
|
||||||
console.log("树形数据加载成功:", treeData.value);
|
console.log("树形数据加载成功:", treeData.value);
|
||||||
|
console.log("原始树形数据结构:", JSON.stringify(res.result, null, 2));
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载树形数据失败:", error);
|
console.error("加载树形数据失败:", error);
|
||||||
@ -328,7 +342,129 @@ const fetchStudentsByClass = async (className: string): Promise<string[]> => {
|
|||||||
return mockStudents;
|
return mockStudents;
|
||||||
};
|
};
|
||||||
|
|
||||||
onLoad((options) => {});
|
onLoad((options) => {
|
||||||
|
console.log("页面加载参数:", options);
|
||||||
|
|
||||||
|
// 如果有接龙ID,说明是编辑模式
|
||||||
|
if (options.id) {
|
||||||
|
noticeId.value = options.id;
|
||||||
|
loadJlData(options.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加页面显示事件处理
|
||||||
|
onShow(() => {
|
||||||
|
console.log("页面显示,当前状态:", {
|
||||||
|
publishedJlId: publishedJlId.value,
|
||||||
|
formDataId: formData.id,
|
||||||
|
isPublishing: isPublishing.value
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果已经发布过且没有编辑ID,说明是从推送清单页面返回
|
||||||
|
// 此时应该重置表单,防止重复发布
|
||||||
|
if (publishedJlId.value && !noticeId.value) {
|
||||||
|
console.log("检测到从推送清单页面返回,重置表单状态");
|
||||||
|
resetForm();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重置表单函数
|
||||||
|
const resetForm = () => {
|
||||||
|
// 重置表单数据
|
||||||
|
Object.assign(formData, {
|
||||||
|
id: "",
|
||||||
|
title: "",
|
||||||
|
content: "",
|
||||||
|
coverImage: null,
|
||||||
|
attachments: [],
|
||||||
|
targetClass: "",
|
||||||
|
targetNames: [],
|
||||||
|
targetStudentIds: [],
|
||||||
|
targetNjIds: [],
|
||||||
|
targetNjmcIds: [],
|
||||||
|
targetBjIds: [],
|
||||||
|
signatureRequired: false,
|
||||||
|
startTime: "",
|
||||||
|
endTime: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重置发布状态
|
||||||
|
publishedJlId.value = null;
|
||||||
|
isPublishing.value = false;
|
||||||
|
noticeId.value = null;
|
||||||
|
|
||||||
|
// 使用 nextTick 确保 DOM 更新后再重置封面
|
||||||
|
nextTick(() => {
|
||||||
|
// 强制重置封面上传组件
|
||||||
|
if (coverUploadRef.value) {
|
||||||
|
// 如果 CustomUpload 组件有重置方法,调用它
|
||||||
|
if (typeof coverUploadRef.value.reset === 'function') {
|
||||||
|
coverUploadRef.value.reset();
|
||||||
|
} else {
|
||||||
|
// 如果没有重置方法,强制触发关闭事件
|
||||||
|
handleCoverClosed("coverImage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("表单已重置,包括封面:", formData.coverImage);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 加载接龙数据(编辑模式使用)
|
||||||
|
const loadJlData = async (jlId: string) => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({ title: "加载数据中..." });
|
||||||
|
|
||||||
|
// 调用获取接龙详情的接口
|
||||||
|
const response = await get(`/api/jl/findById?id=${jlId}`);
|
||||||
|
|
||||||
|
if (response && response.resultCode === 1 && response.result) {
|
||||||
|
const jlData = response.result;
|
||||||
|
|
||||||
|
// 填充表单数据
|
||||||
|
formData.id = jlData.id;
|
||||||
|
formData.title = jlData.jlmc || "";
|
||||||
|
formData.content = jlData.jlms || "";
|
||||||
|
formData.coverImage = jlData.jlfm || null;
|
||||||
|
formData.startTime = jlData.jlkstime || "";
|
||||||
|
formData.endTime = jlData.jljstime || "";
|
||||||
|
formData.signatureRequired = jlData.mdqz === "1";
|
||||||
|
|
||||||
|
// 处理附件
|
||||||
|
if (jlData.jlfj) {
|
||||||
|
const attachmentUrls = jlData.jlfj.split(",");
|
||||||
|
formData.attachments = attachmentUrls.map(url => ({
|
||||||
|
name: url.split("/").pop() || "附件",
|
||||||
|
type: "file",
|
||||||
|
url: url
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理年级和班级信息
|
||||||
|
if (jlData.njId) {
|
||||||
|
formData.targetNjIds = [jlData.njId];
|
||||||
|
}
|
||||||
|
if (jlData.njmcId) {
|
||||||
|
formData.targetNjmcIds = [jlData.njmcId];
|
||||||
|
}
|
||||||
|
if (jlData.bjId) {
|
||||||
|
const bjIds = jlData.bjId.split(",");
|
||||||
|
formData.targetBjIds = bjIds;
|
||||||
|
// 这里需要根据班级ID获取班级名称,暂时使用ID
|
||||||
|
formData.targetClass = bjIds.join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({ title: "数据加载成功", icon: "success" });
|
||||||
|
} else {
|
||||||
|
throw new Error("获取接龙数据失败");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error("加载接龙数据失败:", error);
|
||||||
|
uni.showToast({ title: "加载数据失败", icon: "error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleCoverSelected = async (e: any) => {
|
const handleCoverSelected = async (e: any) => {
|
||||||
if (e.tempFilePaths && e.tempFilePaths.length > 0) {
|
if (e.tempFilePaths && e.tempFilePaths.length > 0) {
|
||||||
@ -492,6 +628,8 @@ const showClassTree = () => {
|
|||||||
// 树形选择确认
|
// 树形选择确认
|
||||||
const onTreeConfirm = async (selectedItems: any[]) => {
|
const onTreeConfirm = async (selectedItems: any[]) => {
|
||||||
console.log("选择的班级:", selectedItems);
|
console.log("选择的班级:", selectedItems);
|
||||||
|
console.log("完整的 selectedItems 数据:", JSON.stringify(selectedItems, null, 2));
|
||||||
|
|
||||||
if (selectedItems.length > 0) {
|
if (selectedItems.length > 0) {
|
||||||
// 处理多选情况
|
// 处理多选情况
|
||||||
const classNames = selectedItems.map((item) => item.title);
|
const classNames = selectedItems.map((item) => item.title);
|
||||||
@ -499,34 +637,81 @@ const onTreeConfirm = async (selectedItems: any[]) => {
|
|||||||
formData.targetNames = [];
|
formData.targetNames = [];
|
||||||
formData.targetStudentIds = [];
|
formData.targetStudentIds = [];
|
||||||
formData.targetNjIds = [];
|
formData.targetNjIds = [];
|
||||||
|
formData.targetNjmcIds = [];
|
||||||
formData.targetBjIds = [];
|
formData.targetBjIds = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
uni.showLoading({ title: "获取学生列表中..." });
|
uni.showLoading({ title: "获取学生列表中..." });
|
||||||
|
|
||||||
// 提取年级ID和班级ID
|
// 提取年级ID、年级名称ID和班级ID
|
||||||
const njIds: string[] = [];
|
const njIds: string[] = [];
|
||||||
|
const njmcIds: string[] = [];
|
||||||
const bjIds: string[] = [];
|
const bjIds: string[] = [];
|
||||||
|
const gradeNames: string[] = []; // 用于存储年级名称,用于错误提示
|
||||||
|
|
||||||
selectedItems.forEach((item) => {
|
selectedItems.forEach((item) => {
|
||||||
// 如果选择的是班级(有parents表示是班级)
|
// 如果选择的是班级(有parents表示是班级)
|
||||||
if (item.parents && item.parents.length > 0) {
|
if (item.parents && item.parents.length > 0) {
|
||||||
const njId = item.parents[0].key; // 年级ID:parents[0].key
|
const parent = item.parents[0]; // 年级信息
|
||||||
|
console.log("年级信息:", parent);
|
||||||
|
|
||||||
|
const njId = parent.key; // 年级ID:parents[0].key
|
||||||
|
const njmcId = parent.njmcId; // 年级名称ID:从年级信息中获取
|
||||||
const bjId = item.key; // 班级ID:item.key
|
const bjId = item.key; // 班级ID:item.key
|
||||||
|
const gradeName = parent.title; // 年级名称
|
||||||
|
|
||||||
|
// 确保年级信息完整
|
||||||
|
if (njId && bjId && gradeName) {
|
||||||
njIds.push(njId);
|
njIds.push(njId);
|
||||||
|
if (njmcId) {
|
||||||
|
njmcIds.push(njmcId);
|
||||||
|
}
|
||||||
bjIds.push(bjId);
|
bjIds.push(bjId);
|
||||||
|
gradeNames.push(gradeName);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`选择班级: ${item.title}, 年级ID: ${njId}, 班级ID: ${bjId}`
|
`选择班级: ${item.title}, 年级ID: ${njId}, 年级名称ID: ${njmcId}, 班级ID: ${bjId}, 年级名称: ${gradeName}`
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
console.warn("年级信息不完整,跳过该班级:", item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn("选择的项目不是班级或缺少年级信息:", item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 检查是否有有效的班级选择
|
||||||
|
if (gradeNames.length === 0) {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: "请选择有效的班级",
|
||||||
|
icon: "none",
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证是否跨年级选择
|
||||||
|
const uniqueGradeNames = [...new Set(gradeNames)];
|
||||||
|
if (uniqueGradeNames.length > 1) {
|
||||||
|
uni.hideLoading();
|
||||||
|
const gradeList = uniqueGradeNames.join("、");
|
||||||
|
uni.showToast({
|
||||||
|
title: `不能跨年级选择发布班级,已选择:${gradeList}`,
|
||||||
|
icon: "none",
|
||||||
|
duration: 4000
|
||||||
|
});
|
||||||
|
return; // 阻止继续执行
|
||||||
|
}
|
||||||
|
|
||||||
if (njIds.length > 0 && bjIds.length > 0) {
|
if (njIds.length > 0 && bjIds.length > 0) {
|
||||||
// 对年级ID去重
|
// 对年级ID去重
|
||||||
const uniqueNjIds = [...new Set(njIds)];
|
const uniqueNjIds = [...new Set(njIds)];
|
||||||
|
const uniqueNjmcIds = [...new Set(njmcIds)];
|
||||||
|
|
||||||
// 保存选择的年级ID和班级ID到formData中
|
// 保存选择的年级ID、年级名称ID和班级ID到formData中
|
||||||
formData.targetNjIds = uniqueNjIds;
|
formData.targetNjIds = uniqueNjIds;
|
||||||
|
formData.targetNjmcIds = uniqueNjmcIds;
|
||||||
formData.targetBjIds = bjIds;
|
formData.targetBjIds = bjIds;
|
||||||
|
|
||||||
// 调用接口获取学生列表
|
// 调用接口获取学生列表
|
||||||
@ -592,30 +777,165 @@ const handleSignatureChange = (e: any) => {
|
|||||||
formData.signatureRequired = index == 1;
|
formData.signatureRequired = index == 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateForm = (): boolean => {
|
const validateForm = () => {
|
||||||
if (!formData.title.trim()) {
|
// 标题校验
|
||||||
|
if (!formData.title || !formData.title.trim()) {
|
||||||
uni.showToast({ title: "请输入通知标题", icon: "none" });
|
uni.showToast({ title: "请输入通知标题", icon: "none" });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!formData.content.trim()) {
|
// 内容校验
|
||||||
|
if (!formData.content || !formData.content.trim()) {
|
||||||
uni.showToast({ title: "请输入通知内容", icon: "none" });
|
uni.showToast({ title: "请输入通知内容", icon: "none" });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (
|
// 年级和班级必填校验
|
||||||
formData.startTime &&
|
if (!formData.targetNjIds.length) {
|
||||||
formData.endTime &&
|
uni.showToast({ title: "请选择年级与班级", icon: "none" });
|
||||||
formData.startTime >= formData.endTime
|
return false;
|
||||||
) {
|
}
|
||||||
|
// 开始时间不能为空
|
||||||
|
if (!formData.startTime) {
|
||||||
|
uni.showToast({ title: "请选择开始时间", icon: "none" });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 结束时间不能为空
|
||||||
|
if (!formData.endTime) {
|
||||||
|
uni.showToast({ title: "请选择结束时间", icon: "none" });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 结束时间必须晚于开始时间
|
||||||
|
const start = new Date(formData.startTime).getTime();
|
||||||
|
const end = new Date(formData.endTime).getTime();
|
||||||
|
if (end <= start) {
|
||||||
uni.showToast({ title: "结束时间必须晚于开始时间", icon: "none" });
|
uni.showToast({ title: "结束时间必须晚于开始时间", icon: "none" });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 其他校验...
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveDraft = () => {
|
const { getUser, getJs } = useUserStore();
|
||||||
|
|
||||||
|
// 获取用户store实例
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
// 获取用户信息作为计算属性
|
||||||
|
const userData = computed(() => getUser);
|
||||||
|
const jsData = computed(() => getJs);
|
||||||
|
|
||||||
|
// 日期格式化函数
|
||||||
|
const formatDate = (dateString: string | null): string | null => {
|
||||||
|
if (!dateString) return null;
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加发布状态标记
|
||||||
|
const isPublishing = ref(false);
|
||||||
|
const publishedJlId = ref<string | null>(null);
|
||||||
|
|
||||||
|
// 构建 JlDto 对象的工具函数
|
||||||
|
const buildJlDto = (status: string) => {
|
||||||
|
// 获取用户信息
|
||||||
|
const user = userData.value;
|
||||||
|
const js = jsData.value;
|
||||||
|
|
||||||
|
console.log("=== 用户信息调试 ===");
|
||||||
|
console.log("userData.value:", user);
|
||||||
|
console.log("jsData.value:", js);
|
||||||
|
console.log("user?.id:", user?.id);
|
||||||
|
console.log("user?.loginName:", user?.loginName);
|
||||||
|
console.log("user?.name:", user?.name);
|
||||||
|
console.log("js?.id:", js?.id);
|
||||||
|
console.log("js?.jsxm:", js?.jsxm);
|
||||||
|
console.log("==================");
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: formData.id || "", // 接龙ID,新增时为空
|
||||||
|
jlmc: formData.title.trim(), // 接龙名称
|
||||||
|
jlms: formData.content.trim(), // 接龙描述
|
||||||
|
jlfm: formData.coverImage || "", // 接龙封面
|
||||||
|
jlfj: formData.attachments.length > 0 ? formData.attachments.map(att => att.url).join(",") : "", // 接龙附件,逗号分隔
|
||||||
|
jlkstime: formatDate(formData.startTime), // 接龙开始时间,格式化为 yyyy-MM-dd HH:mm:ss
|
||||||
|
jljstime: formatDate(formData.endTime), // 接龙结束时间,格式化为 yyyy-MM-dd HH:mm:ss
|
||||||
|
mdqz: formData.signatureRequired ? "1" : "0", // 按名单签字:1启用,0不启用
|
||||||
|
njId: formData.targetNjIds.length > 0 ? formData.targetNjIds[0] : "", // 关联年级ID(取第一个)
|
||||||
|
njmcId: formData.targetNjmcIds.length > 0 ? formData.targetNjmcIds[0] : "", // 关联年级名称ID(取第一个)
|
||||||
|
bjId: formData.targetBjIds, // 关联班级ID列表
|
||||||
|
jlFbr: js?.id || "", // 发布人ID:只从jsData获取
|
||||||
|
jsxm: js?.jsxm || user?.loginName || user?.name || "", // 教师姓名:优先使用jsData的jsxm,其次使用userData的loginName或name
|
||||||
|
createdUserId: user?.id ? parseInt(user.id) : null,
|
||||||
|
createdUserName: user?.loginName || user?.name || "", // 创建用户姓名:只从userData获取
|
||||||
|
jlFbtime: formatDate(new Date().toISOString()), // 发布时间,格式化为 yyyy-MM-dd HH:mm:ss
|
||||||
|
jlStatus: status, // 发布状态:A已发布,B暂存
|
||||||
|
status: "A" // 状态
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveDraft = async () => {
|
||||||
if (!validateForm()) return;
|
if (!validateForm()) return;
|
||||||
console.log("保存草稿", formData);
|
|
||||||
uni.showToast({ title: "草稿保存成功 (模拟)", icon: "success" });
|
try {
|
||||||
|
uni.showLoading({ title: "保存草稿中..." });
|
||||||
|
|
||||||
|
// 准备草稿数据,构建 JlDto 对象
|
||||||
|
const jlDto = buildJlDto("B"); // B表示暂存
|
||||||
|
|
||||||
|
console.log("保存草稿数据:", jlDto);
|
||||||
|
|
||||||
|
// 调用后端接口
|
||||||
|
const response = await post("/api/jl/save", jlDto);
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
|
||||||
|
if (response && response.resultCode === 1) {
|
||||||
|
// 获取接龙ID
|
||||||
|
const jlId = response.result || response.data || jlDto.id;
|
||||||
|
|
||||||
|
// 更新表单中的ID
|
||||||
|
if (jlId) {
|
||||||
|
formData.id = jlId;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "草稿保存成功",
|
||||||
|
icon: "success",
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
|
||||||
|
// 保存成功后返回上一页
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack();
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: response?.resultMsg || "保存草稿失败",
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error("保存草稿失败:", error);
|
||||||
|
|
||||||
|
// 处理数据验证错误
|
||||||
|
if (error instanceof Error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message,
|
||||||
|
icon: "none"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: "保存草稿失败,请重试",
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const previewNotice = () => {
|
const previewNotice = () => {
|
||||||
@ -624,27 +944,109 @@ const previewNotice = () => {
|
|||||||
uni.showToast({ title: "预览功能待实现", icon: "none" });
|
uni.showToast({ title: "预览功能待实现", icon: "none" });
|
||||||
};
|
};
|
||||||
|
|
||||||
const publishNotice = () => {
|
const publishNotice = async () => {
|
||||||
if (!validateForm()) return;
|
if (!validateForm()) return;
|
||||||
|
|
||||||
// 准备发布数据,包含年级ID和班级ID
|
// 检查是否正在发布中
|
||||||
const publishData = {
|
if (isPublishing.value) {
|
||||||
...formData,
|
uni.showToast({ title: "正在发布中,请稍候...", icon: "none" });
|
||||||
njIds: formData.targetNjIds.join(","), // 年级ID字符串
|
return;
|
||||||
bjIds: formData.targetBjIds.join(","), // 班级ID字符串
|
}
|
||||||
};
|
|
||||||
|
|
||||||
console.log("发布通知数据:", publishData);
|
// 检查是否已经发布过
|
||||||
console.log("年级ID:", publishData.njIds);
|
if (publishedJlId.value && !formData.id) {
|
||||||
console.log("班级ID:", publishData.bjIds);
|
uni.showToast({ title: "该接龙已经发布,请勿重复发布", icon: "none" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: 调用发布接口
|
// 如果有ID,检查接龙是否已存在且已发布
|
||||||
// uni.showLoading({ title: "发布中..." });
|
if (formData.id) {
|
||||||
// setTimeout(() => {
|
try {
|
||||||
// uni.hideLoading();
|
const response = await get(`/api/jl/findById?id=${formData.id}`);
|
||||||
// uni.showToast({ title: "发布成功 (模拟)", icon: "success" });
|
if (response && response.resultCode === 1 && response.result) {
|
||||||
// uni.navigateBack();
|
const jlData = response.result;
|
||||||
// }, 1000);
|
if (jlData.jlStatus === 'A') {
|
||||||
|
uni.showToast({ title: "该接龙已经发布,请勿重复发布", icon: "none" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("检查接龙状态失败:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
isPublishing.value = true;
|
||||||
|
uni.showLoading({ title: "发布中..." });
|
||||||
|
|
||||||
|
// 准备发布数据,构建 JlDto 对象
|
||||||
|
const jlDto = buildJlDto("A"); // A表示已发布
|
||||||
|
|
||||||
|
console.log("发布接龙数据:", jlDto);
|
||||||
|
|
||||||
|
// 调用后端接口
|
||||||
|
const response = await post("/api/jl/save", jlDto);
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
|
||||||
|
if (response && response.resultCode === 1) {
|
||||||
|
// 获取接龙ID,优先使用返回的ID,如果没有则使用请求中的ID
|
||||||
|
const jlId = response.result || response.data || jlDto.id;
|
||||||
|
|
||||||
|
console.log("发布成功,接龙ID:", jlId, "完整响应:", response);
|
||||||
|
|
||||||
|
if (!jlId) {
|
||||||
|
console.error("发布成功但未获取到接龙ID");
|
||||||
|
uni.showToast({
|
||||||
|
title: "发布成功,但获取接龙ID失败",
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记为已发布
|
||||||
|
publishedJlId.value = jlId;
|
||||||
|
|
||||||
|
// 更新表单中的ID,防止重复发布
|
||||||
|
formData.id = jlId;
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "发布成功",
|
||||||
|
icon: "success",
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发布成功后跳转到推送清单页面
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/view/notice/push-list?jlId=${jlId}`
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: response?.resultMsg || "发布失败",
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error("发布接龙失败:", error);
|
||||||
|
|
||||||
|
// 处理数据验证错误
|
||||||
|
if (error instanceof Error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: error.message,
|
||||||
|
icon: "none"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: "发布失败,请重试",
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isPublishing.value = false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -1047,6 +1449,12 @@ const publishNotice = () => {
|
|||||||
background-color: #409eff;
|
background-color: #409eff;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: #a0cfff;
|
||||||
|
color: #ffffff;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&.draft-btn:active {
|
&.draft-btn:active {
|
||||||
background-color: #e0e0e0;
|
background-color: #e0e0e0;
|
||||||
|
|||||||
329
src/pages/view/notice/push-list.vue
Normal file
329
src/pages/view/notice/push-list.vue
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
<template>
|
||||||
|
<BasicLayout>
|
||||||
|
<!-- Default slot -->
|
||||||
|
<scroll-view scroll-y class="push-list-scroll-view">
|
||||||
|
<view class="push-list-container">
|
||||||
|
<!-- 页面标题 -->
|
||||||
|
<view class="page-header">
|
||||||
|
<text class="page-title">推送清单</text>
|
||||||
|
<text class="student-count">共 {{ studentList.length }} 名学生</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 学生信息卡片列表 -->
|
||||||
|
<view class="student-cards">
|
||||||
|
<view
|
||||||
|
v-for="(student, index) in studentList"
|
||||||
|
:key="student.id || student.xsId || index"
|
||||||
|
class="student-card"
|
||||||
|
>
|
||||||
|
<view class="student-main-info">
|
||||||
|
<text class="student-name">{{ student.xsxm || student.xm || '未知姓名' }}</text>
|
||||||
|
<text class="grade-class">{{ student.njmc }} {{ student.bjmc }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="parent-info">
|
||||||
|
<text class="parent-label">家长:</text>
|
||||||
|
<text class="parent-name">{{ student.jzxm || '暂无家长信息' }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view v-if="studentList.length === 0" class="empty-state">
|
||||||
|
<uni-icons type="info" size="60" color="#ccc"></uni-icons>
|
||||||
|
<text class="empty-text">暂无学生信息</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- Bottom slot -->
|
||||||
|
<template #bottom>
|
||||||
|
<view class="bottom-actions">
|
||||||
|
<button class="action-btn cancel-btn" @click="handleCancel">
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
<button class="action-btn confirm-btn" @click="handleConfirmPush" :disabled="isPushing">
|
||||||
|
{{ isPushing ? '推送中...' : '确认推送' }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</BasicLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
|
import { jlzxFindByJlParamsApi, xxtsSaveByJlzxParamsApi } from "@/api/base/server";
|
||||||
|
|
||||||
|
interface StudentInfo {
|
||||||
|
id: string;
|
||||||
|
xsId: string;
|
||||||
|
xsxm: string;
|
||||||
|
xm: string;
|
||||||
|
njId: string;
|
||||||
|
njmcId: string;
|
||||||
|
njmc: string;
|
||||||
|
bc: string;
|
||||||
|
bjId: string;
|
||||||
|
bjmc: string;
|
||||||
|
jzIds: string;
|
||||||
|
jzxm: string;
|
||||||
|
jlId: string;
|
||||||
|
jlmc: string;
|
||||||
|
jlts: string;
|
||||||
|
jlwcStatus: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const jlId = ref<string>('');
|
||||||
|
const studentList = ref<StudentInfo[]>([]);
|
||||||
|
const isPushing = ref(false);
|
||||||
|
|
||||||
|
onLoad((options) => {
|
||||||
|
console.log("推送清单页面参数:", options);
|
||||||
|
|
||||||
|
if (options.jlId && options.jlId !== 'null' && options.jlId !== 'undefined') {
|
||||||
|
jlId.value = options.jlId;
|
||||||
|
console.log("获取到接龙ID:", jlId.value);
|
||||||
|
loadStudentList();
|
||||||
|
} else {
|
||||||
|
console.error("缺少接龙ID参数或ID无效:", options.jlId);
|
||||||
|
uni.showToast({ title: "缺少接龙ID参数", icon: "error" });
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack();
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载学生列表
|
||||||
|
const loadStudentList = async () => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({ title: "加载学生信息中..." });
|
||||||
|
|
||||||
|
const response = await jlzxFindByJlParamsApi({ jlId: jlId.value });
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
|
||||||
|
console.log("接口返回数据:", response);
|
||||||
|
|
||||||
|
// 检查是否有学生数据
|
||||||
|
if (response && (response.rows || response.result)) {
|
||||||
|
// 从 rows 字段获取学生列表数据
|
||||||
|
const students = response.rows || response.result || [];
|
||||||
|
studentList.value = students;
|
||||||
|
console.log("学生列表加载成功,共", students.length, "名学生:", studentList.value);
|
||||||
|
} else {
|
||||||
|
console.warn("接口返回数据格式异常:", response);
|
||||||
|
// 如果数据格式异常,尝试直接使用 response 作为数组
|
||||||
|
if (Array.isArray(response)) {
|
||||||
|
studentList.value = response;
|
||||||
|
console.log("直接使用 response 作为学生列表,共", response.length, "名学生");
|
||||||
|
} else {
|
||||||
|
throw new Error("获取学生信息失败:数据格式异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error("加载学生列表失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: error instanceof Error ? error.message : "加载学生信息失败",
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 取消操作
|
||||||
|
const handleCancel = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确认推送
|
||||||
|
const handleConfirmPush = async () => {
|
||||||
|
if (isPushing.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
isPushing.value = true;
|
||||||
|
uni.showLoading({ title: "推送中..." });
|
||||||
|
|
||||||
|
const response = await xxtsSaveByJlzxParamsApi({ jlId: jlId.value });
|
||||||
|
|
||||||
|
uni.hideLoading();
|
||||||
|
isPushing.value = false;
|
||||||
|
|
||||||
|
if (response && response.resultCode === 1) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "推送成功",
|
||||||
|
icon: "success",
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
|
||||||
|
// 推送成功后返回到接龙发布页面(上上页)
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack({
|
||||||
|
delta: 2
|
||||||
|
});
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
throw new Error(response?.resultMsg || "推送失败");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
isPushing.value = false;
|
||||||
|
console.error("推送失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: error instanceof Error ? error.message : "推送失败,请重试",
|
||||||
|
icon: "error"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.push-list-scroll-view {
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.push-list-container {
|
||||||
|
padding: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-count {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-cards {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-card {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-main-info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grade-class {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 8px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parent-name {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 20px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 15px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-top: 1px solid #e5e5e5;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
border-radius: 22px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cancel-btn {
|
||||||
|
background-color: #f4f4f5;
|
||||||
|
color: #909399;
|
||||||
|
border: 1px solid #e9e9eb;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.confirm-btn {
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #3a8ee6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: #c0c4cc;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user