SAAS模式调整
This commit is contained in:
parent
73515ab6a5
commit
e7a726af56
@ -8,6 +8,13 @@ export const checkXsXkApi = async (params: any) => {
|
||||
return await get("/mobile/jz/checkXsXk", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验学生选课并附带告知书内容(聚合接口,KQK 时一次返回选课状态与告知书,减少二次请求)
|
||||
*/
|
||||
export const checkXsXkWithGzsApi = async (params: any) => {
|
||||
return await get("/mobile/jz/checkXsXkWithGzs", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 学生已选课程列表
|
||||
*/
|
||||
|
||||
@ -1,22 +1,32 @@
|
||||
const ip: string = "127.0.0.1:8897";
|
||||
const fwqip: string = "127.0.0.1:8897";
|
||||
//const ip: string = "lzcxsx.cn";
|
||||
//const fwqip: string = "lzcxsx.cn";
|
||||
// 测试环境 / 正式环境:开发时用测试地址,打包时用正式地址
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
const ip: string = isDev ? "127.0.0.1:8897" : "lzcxsx.cn";
|
||||
const fwqip: string = isDev ? "127.0.0.1:8897" : "lzcxsx.cn";
|
||||
|
||||
//const ip: string = "zhxy.yufangzc.com";
|
||||
//const fwqip: string = "zhxy.yufangzc.com";
|
||||
//const ip: string = "lzcxsx.cn";
|
||||
//const fwqip: string = "lzcxsx.cn";
|
||||
|
||||
//打包服务器接口代理标识
|
||||
const SERVERAGENT: string = "/jzd-api";
|
||||
//本地代理url地址,配置了就启动代理,没配置就不启动代理
|
||||
export const HOMEAGENT: string = "";
|
||||
// 开发环境下,手机/平板访问时(非localhost)使用当前页面 host,避免 127.0.0.1 指向设备自身导致请求失败
|
||||
function getDevBaseUrl(): string {
|
||||
if (typeof window !== "undefined" && window.location?.hostname && window.location.hostname !== "localhost" && window.location.hostname !== "127.0.0.1") {
|
||||
return `http://${window.location.hostname}:8897/zhxy`;
|
||||
}
|
||||
return `http://${ip}/zhxy`;
|
||||
}
|
||||
// 接口地址
|
||||
export const BASE_URL: string =
|
||||
process.env.NODE_ENV == "development" ? `http://${ip}/zhxy` : SERVERAGENT;
|
||||
// WebSocket地址
|
||||
export const BASE_WS_URL: string =
|
||||
process.env.NODE_ENV == "development" ? `ws://${ip}` : `wss://${fwqip}`;
|
||||
process.env.NODE_ENV == "development" ? getDevBaseUrl() : SERVERAGENT;
|
||||
// WebSocket地址:开发环境用 ws(无 SSL),正式环境用 wss
|
||||
export const BASE_WS_URL: string = isDev ? `ws://${ip}` : `wss://${ip}`;
|
||||
//图片地址
|
||||
// export const BASE_IMAGE_URL: string = process.env.NODE_ENV == "development" ? `https://${ip}` : `http://${fwqip}`;
|
||||
export const BASE_IMAGE_URL: string = `http://${fwqip}`;
|
||||
// export const BASE_IMAGE_URL: string = process.env.NODE_ENV == "development" ? `https://${ip}` : `https://${fwqip}`;
|
||||
export const BASE_IMAGE_URL: string = `https://${fwqip}`;
|
||||
// kkFileView预览服务地址
|
||||
export const KK_FILE_VIEW_URL: string = `https://${fwqip}/kkpro`;
|
||||
//存token的key
|
||||
@ -33,3 +43,6 @@ export const WHITELIST: WhiteList = [];
|
||||
export const THEMECOLOR: string = "#35468C";
|
||||
// 启动vconsole
|
||||
export const ENABLE_VCONSOLE: boolean = false; // process.env.NODE_ENV != "development";
|
||||
|
||||
/** 就餐报名成功后跳转的支付链接(后续可改为接口/动态配置) */
|
||||
export const JC_PAY_REDIRECT_URL: string = "https://app.xiaoyuan.ccb.com/LHECRESM/V6/JFPAGENEW/index.html#/?schoolId=213245500";
|
||||
|
||||
@ -25,13 +25,15 @@
|
||||
<script lang="ts" setup>
|
||||
import { xkgzsApi } from "@/api/base/server";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
|
||||
const dataStore = useDataStore();
|
||||
import { showLoading } from "@/utils/uniapp";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import RichTextContent from "@/components/RichTextContent/RichTextContent.vue";
|
||||
|
||||
const signCompRef = ref<any>(null);
|
||||
const sign_file = ref<any>(null);
|
||||
const { setFile } = useDataStore();
|
||||
const { setFile } = dataStore;
|
||||
|
||||
const notice = ref("");
|
||||
const lxId = ref("");
|
||||
@ -54,6 +56,12 @@ onLoad(async (options: any) => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 若聚合接口已返回告知书内容,直接使用,避免二次请求
|
||||
const cachedGzs = dataStore.getQk?.gzsContent;
|
||||
if (cachedGzs && (lxId.value === "816059832" || lxId.value === "962488654")) {
|
||||
notice.value = cachedGzs;
|
||||
return;
|
||||
}
|
||||
showLoading({ title: "加载中..." });
|
||||
const res = await xkgzsApi({ kcLx: kcLx });
|
||||
notice.value = res.rows?.[0]?.content || "";
|
||||
@ -73,12 +81,12 @@ async function submit() {
|
||||
});
|
||||
switch (lxId.value) {
|
||||
case "JC": {
|
||||
uni.reLaunch({
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/jc/pay/qrcode",
|
||||
});
|
||||
} break;
|
||||
default: {
|
||||
uni.reLaunch({
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/xk/qk/index?xklxId=" + lxId.value,
|
||||
});
|
||||
}
|
||||
|
||||
@ -97,11 +97,11 @@ interface HomeMenuItem {
|
||||
|
||||
/** 家长端特殊菜单:选课/退费/缴费需 lxId + action,后端 auth_code 映射 */
|
||||
const JZD_SPECIAL_MENU: Record<string, { lxId: string; action: string }> = {
|
||||
"school-xqkxk": { lxId: "962488654", action: "jf" }, // 兴趣课选课
|
||||
"school-jlbxk": { lxId: "816059832", action: "jf" }, // 俱乐部选课
|
||||
"school-jcjf": { lxId: "JC", action: "jf" }, // 就餐报名
|
||||
"school-xqk-tf": { lxId: "962488654", action: "tf" }, // 兴趣课退费
|
||||
"school-jlb-tf": { lxId: "816059832", action: "tf" }, // 俱乐部退费
|
||||
"910010": { lxId: "962488654", action: "jf" }, // 兴趣课选课
|
||||
"910011": { lxId: "816059832", action: "jf" }, // 俱乐部选课
|
||||
"910012": { lxId: "JC", action: "jf" }, // 就餐报名
|
||||
"910013": { lxId: "962488654", action: "tf" }, // 兴趣课退费
|
||||
"910014": { lxId: "816059832", action: "tf" }, // 俱乐部退费
|
||||
};
|
||||
|
||||
// 将菜单树扁平化为家长端结构
|
||||
@ -113,7 +113,7 @@ function flattenMenuToItems(nodes: MobileMenuTreeNode[]): HomeMenuItem[] {
|
||||
const icon = (node.normalCss && /^[a-zA-Z0-9_-]+$/.test(node.normalCss))
|
||||
? `/static/base/home/${node.normalCss}.png`
|
||||
: "/static/base/home/file-text-line.png";
|
||||
const authCode = node.authCode || "";
|
||||
const authCode = node.id + "";
|
||||
const special = authCode ? JZD_SPECIAL_MENU[authCode] : undefined;
|
||||
items.push({
|
||||
id: node.id,
|
||||
@ -160,7 +160,13 @@ const handleMenuClick = debounce(async (item: any) => {
|
||||
if (item.path) {
|
||||
if (item.lxId) {
|
||||
setGlobal({ lxId: item.lxId, action: item.action, from: 'home' });
|
||||
PageUtils.toHome(item.lxId, item.action);
|
||||
uni.showLoading({ title: "加载中...", mask: true });
|
||||
try {
|
||||
await PageUtils.toHome(item.lxId, item.action);
|
||||
uni.hideLoading();
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
}
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: item.path,
|
||||
|
||||
@ -77,6 +77,7 @@ import BasicSign from "@/components/BasicSign/Sign.vue"
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { checkXsJcBmApi, jcXsBmJcApi } from "@/api/base/jcApi";
|
||||
import { JC_PAY_REDIRECT_URL } from "@/config";
|
||||
|
||||
const signCompRef = ref<any>(null);
|
||||
|
||||
@ -171,14 +172,19 @@ const handleJcBzClick = (jcBz: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 跳转到收款码页面
|
||||
// 跳转到支付链接(已报名未缴费时)
|
||||
const navigateToPayment = (jcBz: any) => {
|
||||
// 保存当前选中的标准到store
|
||||
setJcBz(jcBz);
|
||||
// 跳转到收款码预览页面
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/jc/pay/qrcode",
|
||||
});
|
||||
const payUrl = JC_PAY_REDIRECT_URL;
|
||||
try {
|
||||
if (typeof (window as any).plus !== 'undefined' && (window as any).plus.runtime?.openURL) {
|
||||
(window as any).plus.runtime.openURL(payUrl);
|
||||
} else {
|
||||
window.location.href = payUrl;
|
||||
}
|
||||
} catch (_e) {
|
||||
window.location.href = payUrl;
|
||||
}
|
||||
};
|
||||
|
||||
// 跳转到收款码页面
|
||||
@ -221,10 +227,17 @@ const goToQrCode = async (jcBz: any) => {
|
||||
jcBz.paidStatus = 'unpaid';
|
||||
// 保存当前选中的标准到store
|
||||
setJcBz(jcBz);
|
||||
// 跳转到告知书页面
|
||||
uni.navigateTo({
|
||||
url: '/pages/base/jc/pay/qrcode'
|
||||
});
|
||||
// 报名成功后直接跳转支付链接(链接后续可改为动态配置)
|
||||
const payUrl = JC_PAY_REDIRECT_URL;
|
||||
try {
|
||||
if (typeof (window as any).plus !== 'undefined' && (window as any).plus.runtime?.openURL) {
|
||||
(window as any).plus.runtime.openURL(payUrl);
|
||||
} else {
|
||||
window.location.href = payUrl;
|
||||
}
|
||||
} catch (_e) {
|
||||
window.location.href = payUrl;
|
||||
}
|
||||
} else {
|
||||
uni.hideLoading();
|
||||
setTimeout(() => {
|
||||
@ -246,11 +259,14 @@ const goToQrCode = async (jcBz: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 返回首页
|
||||
// 返回(有页面栈则返回上一页,否则回首页)
|
||||
const goHome = () => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/base/home/index'
|
||||
});
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.reLaunch({ url: '/pages/base/home/index' });
|
||||
}
|
||||
};
|
||||
|
||||
// 页面卸载前清除定时器
|
||||
|
||||
@ -47,7 +47,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { checkXsXkApi, getXsXkListApi } from "@/api/base/xkApi";
|
||||
import dayjs from "dayjs";
|
||||
import { getXsXkListApi } from "@/api/base/xkApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
|
||||
@ -83,6 +84,23 @@ const goToWks = () => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 从多个选课中优先选择处于选课时间段的(xkkstime <= now < xkjstime)
|
||||
* @param list 选课列表
|
||||
* @returns 优先选中的选课,若无则返回第一个
|
||||
*/
|
||||
const pickPreferredXk = (list: any[]): any => {
|
||||
if (!list || list.length === 0) return null;
|
||||
if (list.length === 1) return list[0];
|
||||
const now = dayjs().valueOf();
|
||||
const inRange = list.find((xk: any) => {
|
||||
const xkkstime = xk?.xkkstime ? dayjs(xk.xkkstime).valueOf() : 0;
|
||||
const xkjstime = xk?.xkjstime ? dayjs(xk.xkjstime).valueOf() : Infinity;
|
||||
return now >= xkkstime && now < xkjstime;
|
||||
});
|
||||
return inRange ?? list[0];
|
||||
};
|
||||
|
||||
// 加载选课列表
|
||||
const loadXkList = async () => {
|
||||
uni.showLoading({
|
||||
@ -97,7 +115,8 @@ const loadXkList = async () => {
|
||||
const res = await getXsXkListApi(params);
|
||||
if (res.resultCode === 1 && res.result && res.result.length) {
|
||||
xkList.value = res.result;
|
||||
switchXk(res.result[0]);
|
||||
const preferred = pickPreferredXk(res.result);
|
||||
switchXk(preferred ?? res.result[0]);
|
||||
} else {
|
||||
xkList.value = [];
|
||||
switchXk({});
|
||||
@ -113,10 +132,10 @@ const loadXkList = async () => {
|
||||
if (res.resultCode === 1) {
|
||||
const result = res.result || {};
|
||||
if (result.type === 1) {
|
||||
// 有待支付的选课,跳转到支付页面
|
||||
// 有待支付的选课,跳转到支付页面(navigateTo 保留页面栈,支持浏览器返回)
|
||||
setData(result);
|
||||
uni.reLaunch({
|
||||
url: "/pages/base/xk/pay/index",
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/xk/pay/index?xklxId=" + props.xklxId,
|
||||
});
|
||||
return;
|
||||
} else if (result.type === 2 || result.type === 3) {
|
||||
@ -134,7 +153,8 @@ const loadXkList = async () => {
|
||||
|
||||
const initXkList = (list: any) => {
|
||||
xkList.value = list;
|
||||
switchXk(xkList.value[0]);
|
||||
const preferred = pickPreferredXk(list);
|
||||
switchXk(preferred ?? list[0] ?? {});
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
</view>
|
||||
<view class="register-info">
|
||||
<text>报名情况:</text>
|
||||
<text class="register-count">{{ xkkc.hasNum || 0 }}</text>
|
||||
<text class="register-count">{{ getDisplayHasNum(xkkc) }}</text>
|
||||
<text> | {{ xkkc.maxNum || 0 }}</text>
|
||||
</view>
|
||||
<view class="study-time-info" v-if="xkkc.studyTime">
|
||||
@ -45,10 +45,19 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { ref, watch, computed } from "vue";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
const { setKcData, getQk } = useDataStore();
|
||||
|
||||
// 选课开始时间未到时,hasNum 显示为 0
|
||||
const isXkStarted = computed(() => {
|
||||
const xkkstime = props.xk?.xkkstime;
|
||||
if (!xkkstime) return true;
|
||||
return new Date().getTime() >= new Date(xkkstime).getTime();
|
||||
});
|
||||
const getDisplayHasNum = (xkkc: any) => (isXkStarted.value ? (xkkc?.hasNum ?? 0) : 0);
|
||||
const getEffectiveHasNum = (xkkc: any) => (isXkStarted.value ? (xkkc?.hasNum ?? 0) : 0);
|
||||
|
||||
// 接收外部传入属性并设置默认值
|
||||
const props = withDefaults(defineProps<{
|
||||
xk: any,
|
||||
@ -80,9 +89,9 @@ const toggleSelection = (xkkc: any) => {
|
||||
(id: string) => id !== xkkc.id
|
||||
);
|
||||
} else {
|
||||
// 选择课程时的验证逻辑
|
||||
// 选择课程时的验证逻辑(选课未开始前 hasNum 按 0 处理)
|
||||
const maxNum = xkkc.maxNum || 0;
|
||||
const hasNum = xkkc.hasNum || 0;
|
||||
const hasNum = getEffectiveHasNum(xkkc);
|
||||
|
||||
if (maxNum <= hasNum) {
|
||||
uni.showToast({
|
||||
|
||||
@ -75,13 +75,14 @@
|
||||
<view class="divider"></view>
|
||||
|
||||
<view class="content-section">
|
||||
<template v-if="teachingPlan && teachingPlan.length > 0">
|
||||
<template v-if="teachingPlanList.length > 0">
|
||||
<view
|
||||
v-for="(phase, index) in teachingPlan"
|
||||
v-for="(phase, index) in teachingPlanList"
|
||||
:key="index"
|
||||
class="teaching-phase"
|
||||
>
|
||||
<text>{{ phase }}</text>
|
||||
<text class="phase-title">{{ phase.jhjd }}</text>
|
||||
<rich-text v-if="phase.jhnr" :nodes="phase.jhnr" class="phase-content"></rich-text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
@ -92,6 +93,24 @@
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 相关教学附件 -->
|
||||
<view class="info-card" v-if="attachmentList.length > 0">
|
||||
<view class="card-title">相关教学附件</view>
|
||||
<view class="divider"></view>
|
||||
<view class="attachment-list">
|
||||
<view
|
||||
v-for="(att, index) in attachmentList"
|
||||
:key="index"
|
||||
class="attachment-item"
|
||||
@click="openAttachment(att)"
|
||||
>
|
||||
<u-icon name="file-text" size="20" color="#2879ff"></u-icon>
|
||||
<text class="attachment-name">{{ att.name }}</text>
|
||||
<u-icon name="arrow-right" size="14" color="#c0c4cc"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<template #bottom>
|
||||
<view class="white-bg-color py-5">
|
||||
@ -111,14 +130,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from "vue";
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { navigateBack } from "@/utils/uniapp";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { imagUrl } from "@/utils";
|
||||
import { kcjhFindKcjhByKcIdApi } from "@/api/base/server";
|
||||
import { getXkkcDetailByIdApi } from "@/api/base/xkApi";
|
||||
|
||||
// 定义课程数据类型
|
||||
// 定义课程数据类型(yfzc_xkkc 表字段)
|
||||
interface CourseData {
|
||||
id?: string;
|
||||
kcmc?: string;
|
||||
@ -128,33 +147,76 @@ interface CourseData {
|
||||
studyTime?: string;
|
||||
kcjsms?: string;
|
||||
jxll?: string;
|
||||
jxjh?: string;
|
||||
fileUrl?: string;
|
||||
fileName?: string;
|
||||
fileFormat?: string;
|
||||
remark?: string;
|
||||
lxtp?: string;
|
||||
[key: string]: any; // 允许其他字段
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const useData = useDataStore();
|
||||
const { kcData } = storeToRefs(useData);
|
||||
|
||||
// 教学计划 - 此处假设没有具体教学计划字段,将备注内容拆分为教学计划
|
||||
const teachingPlan = ref<string[]>([]);
|
||||
// 从 getXkkcDetailById 接口获取的完整 xkkc 数据
|
||||
const xkkcDetail = ref<CourseData>({});
|
||||
|
||||
const courseData = kcData.value as CourseData;
|
||||
if (courseData && courseData.id) {
|
||||
kcjhFindKcjhByKcIdApi({
|
||||
xkkcId: courseData.id,
|
||||
}).then((res) => {
|
||||
if (res.resultCode == 1) {
|
||||
teachingPlan.value = res.result.map(
|
||||
(item: any) => item.jhjd + ":" + item.jhms
|
||||
);
|
||||
}
|
||||
// 将 HTML 中 img 的 src 转为完整 URL,供 rich-text 正确加载图片
|
||||
const processHtmlForRichText = (html: string): string => {
|
||||
if (!html) return "";
|
||||
return html.replace(/<img\s+([^>]*?)src=["']([^"']+)["']([^>]*)>/gi, (_, before, src, after) => {
|
||||
const fullSrc = imagUrl(src);
|
||||
const style = 'style="max-width:100%;height:auto"';
|
||||
return `<img ${before}src="${fullSrc}" ${style}${after}>`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 教学计划 - 从 xkkc.jxjh 解析,保留 HTML(含图片)用 rich-text 展示
|
||||
// 格式: [{"value":{"jhjd":"第1次课","jhnr":"<p><img src=\"...\">内容</p>"}}, ...]
|
||||
const teachingPlanList = computed(() => {
|
||||
const jxjh = xkkcDetail.value.jxjh;
|
||||
if (!jxjh) return [];
|
||||
try {
|
||||
const parsed = JSON.parse(jxjh);
|
||||
if (Array.isArray(parsed)) {
|
||||
return parsed
|
||||
.map((item: any) => {
|
||||
const v = item?.value ?? item;
|
||||
const jhjd = v.jhjd || "";
|
||||
const rawJhnr = v.jhnr || v.jhms || "";
|
||||
const jhnr = rawJhnr ? processHtmlForRichText(rawJhnr) : "";
|
||||
return { jhjd, jhnr };
|
||||
})
|
||||
.filter((p: { jhjd: string; jhnr: string }) => p.jhjd || p.jhnr);
|
||||
}
|
||||
} catch {
|
||||
// 非 JSON,忽略
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// 教学相关附件 - 从 xkkc.fileUrl、fileName、fileFormat 解析
|
||||
const attachmentList = computed(() => {
|
||||
const fileUrl = xkkcDetail.value.fileUrl || "";
|
||||
const fileName = xkkcDetail.value.fileName || "";
|
||||
const urls = fileUrl.split(",").map((s) => s.trim()).filter(Boolean);
|
||||
const names = fileName.split(";").map((s) => s.trim());
|
||||
return urls.map((url, i) => ({ url, name: names[i] || `附件${i + 1}` }));
|
||||
});
|
||||
|
||||
// 打开附件(通过 webView 打开或下载)
|
||||
const openAttachment = (att: { url: string; name: string }) => {
|
||||
const fullUrl = imagUrl(att.url);
|
||||
if (!fullUrl) return;
|
||||
uni.navigateTo({
|
||||
url: "/pages/system/webView/webView?url=" + encodeURIComponent(fullUrl),
|
||||
});
|
||||
};
|
||||
|
||||
// 课程详情数据
|
||||
const courseDetail = computed(() => {
|
||||
const data = (kcData.value as CourseData) || {};
|
||||
const data = xkkcDetail.value || {};
|
||||
return {
|
||||
id: data.id || "",
|
||||
title: data.kcmc || "暂无课程名称",
|
||||
@ -162,28 +224,41 @@ const courseDetail = computed(() => {
|
||||
location: data.kcdd || "暂无地点信息",
|
||||
price: data.kcje || 0,
|
||||
studyTime: data.studyTime || "暂无上课时间",
|
||||
lxtp: imagUrl(data.lxtp || ''), // 使用imagUrl处理图片路径
|
||||
lxtp: imagUrl(data.lxtp || ""),
|
||||
};
|
||||
});
|
||||
|
||||
// 教师信息
|
||||
const teacherInfo = computed(() => {
|
||||
const data = (kcData.value as CourseData) || {};
|
||||
|
||||
// 处理多个教师头像的情况
|
||||
const teacherAvatars = data.jstx ? data.jstx.split(',').map((avatar: string) => avatar.trim()) : [];
|
||||
|
||||
const data = xkkcDetail.value || {};
|
||||
const teacherAvatars = data.jstx ? data.jstx.split(",").map((a: string) => a.trim()) : [];
|
||||
return {
|
||||
name: data.jsName || "暂无教师信息",
|
||||
avatar: teacherAvatars.length > 0 ? imagUrl(teacherAvatars[0]) : '', // 只取第一个头像
|
||||
avatar: teacherAvatars.length > 0 ? imagUrl(teacherAvatars[0]) : "",
|
||||
introduction: data.kcjsms || "暂无教师介绍",
|
||||
};
|
||||
});
|
||||
|
||||
// 教学理念
|
||||
const teachingPhilosophy = computed(() => {
|
||||
const data = (kcData.value as CourseData) || {};
|
||||
return data.jxll || "暂无教学理念信息";
|
||||
// 教学理念 - 从 xkkc.jxll
|
||||
const teachingPhilosophy = computed(() => xkkcDetail.value.jxll || "暂无教学理念信息");
|
||||
|
||||
// 进入详情页时,通过 getXkkcDetailById 获取完整 xkkc(含 jxll、jxjh、fileUrl 等)
|
||||
onMounted(() => {
|
||||
const data = kcData.value as CourseData;
|
||||
const id = data?.id;
|
||||
if (id) {
|
||||
getXkkcDetailByIdApi(id).then((res) => {
|
||||
if (res.resultCode === 1 && res.result) {
|
||||
xkkcDetail.value = res.result;
|
||||
} else {
|
||||
xkkcDetail.value = { ...data };
|
||||
}
|
||||
}).catch(() => {
|
||||
xkkcDetail.value = { ...data };
|
||||
});
|
||||
} else {
|
||||
xkkcDetail.value = { ...data } || {};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -351,13 +426,27 @@ const teachingPhilosophy = computed(() => {
|
||||
line-height: 1.6;
|
||||
|
||||
.teaching-phase {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.phase-title {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.phase-content {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.empty-data {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -372,6 +461,26 @@ const teachingPhilosophy = computed(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-list {
|
||||
.attachment-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-name {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-action {
|
||||
padding: 15px;
|
||||
margin-top: 20px;
|
||||
|
||||
@ -30,8 +30,8 @@
|
||||
</view>
|
||||
|
||||
<view class="action-buttons">
|
||||
<view class="cancel-btn" @click="cancelRegistration">
|
||||
取消报名
|
||||
<view class="cancel-btn" @click="goBack">
|
||||
返回
|
||||
</view>
|
||||
<view class="pay-btn" :class="{ 'pay-btn--disabled': isPaySubmitting }" @click="payNow">
|
||||
{{ isPaySubmitting ? '支付中...' : '立即支付' }}
|
||||
@ -54,9 +54,6 @@ const { getQk } = useDataStore();
|
||||
// 为支付按钮创建防抖函数和状态
|
||||
const { isProcessing: isPaySubmitting, debounce: payDebounce } = useDebounce(2000);
|
||||
|
||||
// 为取消报名按钮创建防抖函数和状态
|
||||
const { isProcessing: isCancelSubmitting, debounce: cancelDebounce } = useDebounce(2000);
|
||||
|
||||
const qk = ref<any>({});
|
||||
// 总金额
|
||||
const totalJe = ref(0);
|
||||
@ -168,40 +165,13 @@ const startCountdown = () => {
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// 返回上一页
|
||||
// 返回首页
|
||||
const goBack = () => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/base/home/index'
|
||||
});
|
||||
};
|
||||
|
||||
// 取消报名
|
||||
const cancelRegistration = () => {
|
||||
uni.showModal({
|
||||
title: "取消报名",
|
||||
content: "确定要取消报名吗?",
|
||||
success: cancelDebounce(async (res) => {
|
||||
if (res.confirm) {
|
||||
try {
|
||||
const res = await jzXkCancelApi({
|
||||
xsId: qk.value.xsId,
|
||||
xkId: qk.value.xkId
|
||||
});
|
||||
if (res.resultCode === 1) {
|
||||
uni.showLoading({ title: "取消报名中,请稍后..." });
|
||||
} else {
|
||||
uni.showToast({ title: res.message, icon: "none" });
|
||||
goBack();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
goBack();
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
// 立即支付
|
||||
const payNow = payDebounce(async () => {
|
||||
try {
|
||||
|
||||
@ -74,9 +74,12 @@ const checkEnrollmentStatus = (xk: any) => {
|
||||
const endTime = new Date(xk.xkjstime).getTime();
|
||||
|
||||
if (now > endTime) {
|
||||
// 选课已结束,跳转到结束页面
|
||||
// 选课已结束,用 redirectTo 替换当前页,避免浏览器返回时回到选课页
|
||||
console.log('[qk/index] 跳转 yjz,xk 数据:', xk);
|
||||
console.log('[qk/index] xk 字段:', { xkmc: xk?.xkmc, xkkstime: xk?.xkkstime, xkjstime: xk?.xkjstime });
|
||||
const courseInfo = encodeURIComponent(JSON.stringify(xk));
|
||||
uni.navigateTo({
|
||||
console.log('[qk/index] courseInfo 长度:', courseInfo.length);
|
||||
uni.redirectTo({
|
||||
url: `/pages/base/xk/qk/yjz?courseInfo=${courseInfo}`
|
||||
});
|
||||
return true;
|
||||
@ -119,11 +122,14 @@ const changeXkkc = (ids: any) => {
|
||||
selectedXkkcIds.value = ids;
|
||||
}
|
||||
|
||||
// 回到首页
|
||||
// 返回(有页面栈则返回上一页,否则回首页)
|
||||
const goBack = () => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/base/home/index'
|
||||
});
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.reLaunch({ url: '/pages/base/home/index' });
|
||||
}
|
||||
}
|
||||
|
||||
// 提交选课
|
||||
|
||||
@ -59,9 +59,11 @@ const checkEnrollmentStatus = (xk: any) => {
|
||||
const endTime = new Date(xk.xkjstime).getTime();
|
||||
|
||||
if (now > endTime) {
|
||||
// 选课已结束,跳转到结束页面
|
||||
// 选课已结束,用 redirectTo 替换当前页,避免浏览器返回时回到选课页
|
||||
console.log('[qk/jlb] 跳转yjz,传入的xk:', xk);
|
||||
console.log('[qk/jlb] xk.xkmc:', xk?.xkmc, 'xk.xkkstime:', xk?.xkkstime, 'xk.xkjstime:', xk?.xkjstime);
|
||||
const courseInfo = encodeURIComponent(JSON.stringify(xk));
|
||||
uni.navigateTo({
|
||||
uni.redirectTo({
|
||||
url: `/pages/base/xk/qk/yjz?courseInfo=${courseInfo}`
|
||||
});
|
||||
return true;
|
||||
|
||||
@ -53,11 +53,14 @@ const { getData } = useDataStore();
|
||||
|
||||
const title = ref("");
|
||||
|
||||
// 返回首页
|
||||
// 返回(有页面栈则返回上一页,否则回首页)
|
||||
const goHome = () => {
|
||||
uni.reLaunch({
|
||||
url: "/pages/base/home/index"
|
||||
});
|
||||
const pages = getCurrentPages();
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.reLaunch({ url: "/pages/base/home/index" });
|
||||
}
|
||||
};
|
||||
|
||||
onLoad((options:any) => {
|
||||
|
||||
@ -60,7 +60,10 @@ const checkEnrollmentStatus = (xk: any) => {
|
||||
|
||||
if (now > endTime) {
|
||||
// 选课已结束,跳转到结束页面
|
||||
console.log('[qk/xqk] 跳转 yjz,xk 数据:', xk);
|
||||
console.log('[qk/xqk] xk 字段:', { xkmc: xk?.xkmc, xkkstime: xk?.xkkstime, xkjstime: xk?.xkjstime });
|
||||
const courseInfo = encodeURIComponent(JSON.stringify(xk));
|
||||
console.log('[qk/xqk] courseInfo 长度:', courseInfo.length);
|
||||
uni.navigateTo({
|
||||
url: `/pages/base/xk/qk/yjz?courseInfo=${courseInfo}`
|
||||
});
|
||||
|
||||
@ -72,8 +72,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { onLoad, onBackPress } from '@dcloudio/uni-app';
|
||||
|
||||
// 页面参数
|
||||
const courseInfo = ref<any>(null);
|
||||
@ -81,14 +81,46 @@ const courseInfo = ref<any>(null);
|
||||
onLoad((options) => {
|
||||
// 接收传递过来的选课信息
|
||||
if (options?.courseInfo) {
|
||||
const raw = options.courseInfo;
|
||||
try {
|
||||
courseInfo.value = JSON.parse(decodeURIComponent(options.courseInfo));
|
||||
} catch (error) {
|
||||
console.error('解析选课信息失败:', error);
|
||||
// 框架可能已对 URL 参数自动解码,先尝试直接解析(避免 decodeURIComponent 对已解码字符串报 URI malformed)
|
||||
courseInfo.value = JSON.parse(raw);
|
||||
} catch (_e1) {
|
||||
try {
|
||||
courseInfo.value = JSON.parse(decodeURIComponent(raw));
|
||||
} catch (e2) {
|
||||
console.error('解析选课信息失败:', e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 返回(始终回到首页,避免返回到选课界面陷入循环)
|
||||
const goBack = () => {
|
||||
uni.reLaunch({ url: '/pages/base/home/index' });
|
||||
};
|
||||
|
||||
// 拦截导航栏返回、App 物理返回键
|
||||
onBackPress(() => {
|
||||
goBack();
|
||||
return true; // 阻止默认返回
|
||||
});
|
||||
|
||||
// H5:拦截浏览器返回键(onBackPress 在 H5 不支持浏览器返回)
|
||||
let popstateHandler: (() => void) | null = null;
|
||||
onMounted(() => {
|
||||
if (typeof window !== 'undefined' && window.history?.pushState) {
|
||||
popstateHandler = () => goBack();
|
||||
window.history.pushState(null, '', document.URL);
|
||||
window.addEventListener('popstate', popstateHandler);
|
||||
}
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (typeof window !== 'undefined' && popstateHandler) {
|
||||
window.removeEventListener('popstate', popstateHandler);
|
||||
}
|
||||
});
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: string) => {
|
||||
if (!time) return '未设置';
|
||||
@ -102,13 +134,6 @@ const formatTime = (time: string) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 返回首页
|
||||
const goBack = () => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/base/home/index'
|
||||
});
|
||||
};
|
||||
|
||||
// 刷新页面
|
||||
const refreshPage = () => {
|
||||
uni.reLaunch({
|
||||
|
||||
@ -32,11 +32,12 @@
|
||||
</view>
|
||||
|
||||
<!-- 附件预览 -->
|
||||
<view v-if="taskInfo.fileUrl && taskInfo.fileName" class="file-preview mt-15">
|
||||
<view class="file-preview-item">
|
||||
<text class="file-label">附件:</text>
|
||||
<text class="file-name" @click="previewFile(taskInfo.fileUrl)">{{ taskInfo.fileName }}</text>
|
||||
</view>
|
||||
<view v-if="taskInfo.fileUrl" class="file-preview mt-15">
|
||||
<BasicFilePreview
|
||||
:file-url="taskInfo.fileUrl"
|
||||
:file-name="taskInfo.fileName"
|
||||
:file-format="taskInfo.fileFormat"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@ -77,7 +78,7 @@
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { onLoad } from '@dcloudio/uni-app';
|
||||
import { zpFindDetailByIdApi } from "@/api/base/server";
|
||||
import { imagUrl } from "@/utils";
|
||||
import BasicFilePreview from "@/components/BasicFile/preview.vue";
|
||||
|
||||
interface TaskInfo {
|
||||
id?: string;
|
||||
@ -87,6 +88,7 @@ interface TaskInfo {
|
||||
zpjstime?: string;
|
||||
fileUrl?: string;
|
||||
fileName?: string;
|
||||
fileFormat?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@ -189,21 +191,6 @@ const getTaskTypeText = (type?: string) => {
|
||||
};
|
||||
return typeMap[type || ''] || '未知类型';
|
||||
};
|
||||
|
||||
// 预览文件
|
||||
const previewFile = (fileUrl: string) => {
|
||||
if (!fileUrl) return;
|
||||
const fullUrl = imagUrl(fileUrl);
|
||||
const fileExt = fileUrl.split('.').pop()?.toLowerCase();
|
||||
if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(fileExt || '')) {
|
||||
uni.previewImage({
|
||||
urls: [fullUrl],
|
||||
current: fullUrl
|
||||
});
|
||||
} else {
|
||||
uni.showToast({ title: '请下载查看', icon: 'none' });
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -391,36 +378,11 @@ const previewFile = (fileUrl: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 文件预览样式
|
||||
// 文件预览样式(BasicFilePreview 组件内部样式,此处仅保留外层容器)
|
||||
.file-preview {
|
||||
padding: 12px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e9ecef;
|
||||
|
||||
.file-preview-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.file-label {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
color: #4e73df;
|
||||
font-size: 14px;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
word-break: break-all;
|
||||
flex: 1;
|
||||
|
||||
&:active {
|
||||
color: #2e59d9;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -354,7 +354,7 @@ const generateFormSchema = () => {
|
||||
}
|
||||
};
|
||||
} else if (zplx.zpfl === "scsp") {
|
||||
// 上传视频
|
||||
// 上传视频(视频大小限制 50MB)
|
||||
componentConfig = {
|
||||
component: "ImageVideoUpload",
|
||||
componentProps: {
|
||||
@ -363,7 +363,13 @@ const generateFormSchema = () => {
|
||||
enableFile: false,
|
||||
maxVideoCount: 30,
|
||||
uploadApi: attachmentUpload,
|
||||
compressConfig: COMPRESS_PRESETS.medium,
|
||||
compressConfig: {
|
||||
...COMPRESS_PRESETS.medium,
|
||||
video: {
|
||||
...COMPRESS_PRESETS.medium.video,
|
||||
maxSize: 50 * 1024 * 1024
|
||||
}
|
||||
},
|
||||
videoList: formData.value[fieldName] || [],
|
||||
showSectionTitle: false
|
||||
}
|
||||
|
||||
@ -2,9 +2,9 @@ import { defineStore } from "pinia";
|
||||
import type { MobileMenuTreeNode } from "@/api/system/menu";
|
||||
|
||||
export const useMenuStore = defineStore({
|
||||
id: "app-Menu",
|
||||
id: "app-Menu-jzd",
|
||||
state: () => ({
|
||||
/** 树形菜单数据,持久化到 localStorage key: app-Menu */
|
||||
/** 树形菜单数据,持久化到 localStorage key: app-Menu-jzd(家长端,与教师端 app-Menu-jsd 区分,避免同域下菜单互相覆盖) */
|
||||
mobileMenu: [] as MobileMenuTreeNode[],
|
||||
}),
|
||||
getters: {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { checkXsXkApi, checkXkTfApi } from "@/api/base/xkApi";
|
||||
import { checkXsXkWithGzsApi, checkXkTfApi } from "@/api/base/xkApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
const userStore = useUserStore();
|
||||
@ -75,7 +75,7 @@ export const PageUtils = {
|
||||
* @param xklxId
|
||||
*/
|
||||
async checkQkLogic(xklxId: string) {
|
||||
const res: any = await checkXsXkApi({
|
||||
const res: any = await checkXsXkWithGzsApi({
|
||||
xsId: userStore.getCurXs.id,
|
||||
njmcId: userStore.getCurXs.njmcId,
|
||||
xklxId: xklxId,
|
||||
@ -94,35 +94,35 @@ export const PageUtils = {
|
||||
const result = res.result || {};
|
||||
// 记录到缓存数据中
|
||||
dataStore.setQk(result);
|
||||
// 状态判断
|
||||
// 状态判断(使用 navigateTo 保留页面栈,支持浏览器返回)
|
||||
switch (result.xsXkStatus) {
|
||||
case 'WFB': { // KQK可抢课
|
||||
uni.reLaunch({
|
||||
case 'WFB': { // 未开放
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/xk/qk/wks?xklxId=" + xklxId,
|
||||
});
|
||||
} break;
|
||||
case 'KQK': { // KQK可抢课
|
||||
uni.reLaunch({
|
||||
case 'KQK': { // 可抢课-告知书
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/gzs/index?lxId=" + xklxId,
|
||||
});
|
||||
} break;
|
||||
case 'QKZ': { // QKZ抢课中
|
||||
uni.reLaunch({
|
||||
case 'QKZ': { // 抢课中
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/xk/qk/index?xklxId=" + xklxId,
|
||||
});
|
||||
} break;
|
||||
case 'YQK': { // YQK已选课
|
||||
uni.reLaunch({
|
||||
case 'YQK': { // 已选课
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/xk/pay/index?xklxId=" + xklxId,
|
||||
});
|
||||
} break;
|
||||
case 'DZF': { // DZF待支付
|
||||
uni.reLaunch({
|
||||
case 'DZF': { // 待支付
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/xk/pay/index?xklxId=" + xklxId,
|
||||
});
|
||||
} break;
|
||||
case 'YZF': { // YZF已支付
|
||||
uni.reLaunch({
|
||||
case 'YZF': { // 已支付
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/xk/pay/success?xklxId=" + xklxId,
|
||||
});
|
||||
} break;
|
||||
@ -166,9 +166,9 @@ export const PageUtils = {
|
||||
}
|
||||
},
|
||||
|
||||
// 判断就餐逻辑
|
||||
// 判断就餐逻辑(使用 navigateTo 保留页面栈,支持浏览器返回)
|
||||
async checkJcLogic() {
|
||||
uni.reLaunch({
|
||||
uni.navigateTo({
|
||||
url: "/pages/base/jc/bm",
|
||||
});
|
||||
},
|
||||
|
||||
@ -120,14 +120,14 @@ export function get<T = any>(
|
||||
} else {
|
||||
if (res.resultCode) {
|
||||
if (res.resultCode != RESULT_CODE_NOT_LOGIN) {
|
||||
showToast(res.message || "接口异常");
|
||||
showToast(res.message || "接口异常1");
|
||||
reject(res);
|
||||
}
|
||||
} else {
|
||||
if (res.rows) {
|
||||
resolve(res);
|
||||
} else {
|
||||
showToast(res.message || "接口异常");
|
||||
showToast(res.message || "接口异常2");
|
||||
reject(res);
|
||||
}
|
||||
}
|
||||
@ -169,14 +169,14 @@ export function post<T = any>(
|
||||
} else {
|
||||
if (res.resultCode) {
|
||||
if (res.resultCode != RESULT_CODE_NOT_LOGIN) {
|
||||
showToast(res.message || "接口异常");
|
||||
showToast(res.message || "接口异常3");
|
||||
reject(res);
|
||||
}
|
||||
} else {
|
||||
if (res.rows) {
|
||||
resolve(res);
|
||||
} else {
|
||||
showToast(res.message || "接口异常");
|
||||
showToast(res.message || "接口异常4");
|
||||
reject(res);
|
||||
}
|
||||
}
|
||||
@ -216,14 +216,14 @@ export function file(
|
||||
} else {
|
||||
if (res.resultCode) {
|
||||
if (res.resultCode != RESULT_CODE_NOT_LOGIN) {
|
||||
showToast(res.message || "接口异常");
|
||||
showToast(res.message || "接口异常5");
|
||||
reject(res);
|
||||
}
|
||||
} else {
|
||||
if (res.rows) {
|
||||
resolve(res);
|
||||
} else {
|
||||
showToast(res.message || "接口异常");
|
||||
showToast(res.message || "接口异常6");
|
||||
reject(res);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user