|
|
@@ -1,103 +1,103 @@
|
|
|
<template>
|
|
|
- <view class="p-30">
|
|
|
- <uv-read-more ref="more" show-height="120" close-text="展开全部" toggle>
|
|
|
- <uv-parse :content="baseInfo.introduction" content-style="color:#1A1A1A; font-size: 28rpx"
|
|
|
- container-style="padding:30rpx; border-radius: 24rpx; background-color: var(--back-light)"/>
|
|
|
- </uv-read-more>
|
|
|
- <view class="mt-30 flex justify-between items-center text-28 gap-20">
|
|
|
- <view class="bg-secondary-light text-secondary" :class="buttonClass" @click="handleWebsite">
|
|
|
- <uv-icon name="share-fill" color="var(--secondary)"/>
|
|
|
- <text class="text-secondary">招生官网</text>
|
|
|
- </view>
|
|
|
- <view class="bg-primary-100 text-primary" :class="buttonClass" @click="handlePhone">
|
|
|
- <uv-icon name="phone-fill" color="primary"/>
|
|
|
- <text class="text-primary">招生电话</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view v-if="baseInfo.images?.length" class="mt-30">
|
|
|
- <view class="flex justify-between items-center">
|
|
|
- <view class="text-32 font-bold text-fore-title">院校风采</view>
|
|
|
- <view v-if="baseInfo.images.length>4" class="flex items-center text-24 text-fore-tip"
|
|
|
- @click="$refs.imagesPopup.open()">
|
|
|
- 查看更多
|
|
|
- <uv-icon name="arrow-right" color="info"/>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
- <view class="mt-28 grid grid-cols-2 gap-28">
|
|
|
- <ie-image v-for="(item,i) in baseInfo.images.slice(0,4)" :key="i" :src="item.url" mode="aspectFill"
|
|
|
- :round="6" custom-class="w-335 h-200" @click="handlePreview(i)"/>
|
|
|
- </view>
|
|
|
+ <view class="p-30">
|
|
|
+ <uv-read-more ref="more" show-height="120" close-text="展开全部" toggle>
|
|
|
+ <uv-parse :content="baseInfo.introduction" content-style="color:#1A1A1A; font-size: 28rpx"
|
|
|
+ container-style="padding:30rpx; border-radius: 24rpx; background-color: var(--back-light)" />
|
|
|
+ </uv-read-more>
|
|
|
+ <view class="mt-30 flex justify-between items-center text-28 gap-20">
|
|
|
+ <view class="bg-secondary-light text-secondary" :class="buttonClass" @click="handleWebsite">
|
|
|
+ <uv-icon name="share-fill" color="var(--secondary)" />
|
|
|
+ <text class="text-secondary">招生官网</text>
|
|
|
+ </view>
|
|
|
+ <view class="bg-primary-100 text-primary" :class="buttonClass" @click="handlePhone">
|
|
|
+ <uv-icon name="phone-fill" color="primary" />
|
|
|
+ <text class="text-primary">招生电话</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view v-if="baseInfo.images?.length" class="mt-30">
|
|
|
+ <view class="flex justify-between items-center">
|
|
|
+ <view class="text-32 font-bold text-fore-title">院校风采</view>
|
|
|
+ <view v-if="baseInfo.images.length > 4" class="flex items-center text-24 text-fore-tip"
|
|
|
+ @click="handleMore">
|
|
|
+ 查看更多
|
|
|
+ <uv-icon name="arrow-right" color="info" />
|
|
|
</view>
|
|
|
- <view class="mt-30">
|
|
|
- <view class="text-32 font-bold text-fore-title">开设专业</view>
|
|
|
- <uv-gap v-if="loading" height="15"/>
|
|
|
- <uv-skeleton v-if="loading" :title="false" rows="3" rows-height="30" :rows-width="['100%','100%','100%']"/>
|
|
|
- <view v-for="(g,i) in grouped" :key="i"
|
|
|
- class="mt-28 p-28 flex justify-between items-center bg-back-light rounded-lg">
|
|
|
- <view class="text-28 font-bold text-fore-title truncate">{{ g.root.name }}</view>
|
|
|
- <view class="text-24 text-fore-title flex items-center" @click="handleProfessionGroup(g)">
|
|
|
- <text>{{ g.count }}个专业</text>
|
|
|
- <uv-icon name="arrow-right"/>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
+ </view>
|
|
|
+ <view class="mt-28 grid grid-cols-2 gap-28">
|
|
|
+ <ie-image v-for="(item, i) in baseInfo.images.slice(0, 4)" :key="i" :src="item.url" mode="aspectFill" :round="6"
|
|
|
+ custom-class="w-335 h-200" @click="handlePreview(i)" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="mt-30">
|
|
|
+ <view class="text-32 font-bold text-fore-title">开设专业</view>
|
|
|
+ <uv-gap v-if="loading" height="15" />
|
|
|
+ <uv-skeleton v-if="loading" :title="false" rows="3" rows-height="30" :rows-width="['100%', '100%', '100%']" />
|
|
|
+ <view v-for="(g, i) in grouped" :key="i"
|
|
|
+ class="mt-28 p-28 flex justify-between items-center bg-back-light rounded-lg">
|
|
|
+ <view class="text-28 font-bold text-fore-title truncate">{{ g.root.name }}</view>
|
|
|
+ <view class="text-24 text-fore-title flex items-center" @click="handleProfessionGroup(g)">
|
|
|
+ <text>{{ g.count }}个专业</text>
|
|
|
+ <uv-icon name="arrow-right" />
|
|
|
</view>
|
|
|
- <!-- <uv-action-sheet ref="actionSheet" :title="baseInfo.tel" :actions="actions" safe-area-inset-bottom-->
|
|
|
- <!-- close-on-click-overlay cancel-text="取消" @select="handleActionSelect"/>-->
|
|
|
- <ie-popup ref="popup" :show-toolbar="false">
|
|
|
- <template v-if="popupGroup">
|
|
|
- <view class="h-90 flex justify-center items-center text-32 font-bold">{{ popupGroup.root.name }}</view>
|
|
|
- <scroll-view scroll-y :style="{maxHeight: '50vh', backgroundColor: 'var(--back-light)'}">
|
|
|
- <uv-cell-group v-for="(g,i) in popupGroup.subGroups" :key="i" title="1">
|
|
|
- <template #title>
|
|
|
- <text class="text-24 text-fore-tip">{{ g.parent.name }}</text>
|
|
|
- </template>
|
|
|
- <uv-cell v-for="p in g.list" :title="p.name" custom-class="bg-white"/>
|
|
|
- </uv-cell-group>
|
|
|
- </scroll-view>
|
|
|
- </template>
|
|
|
- </ie-popup>
|
|
|
- <ie-popup ref="imagesPopup" :show-toolbar="false">
|
|
|
- <view class="h-90 flex justify-center items-center text-32 font-bold">院校风采</view>
|
|
|
- <scroll-view scroll-y :style="{maxHeight: '50vh', backgroundColor: 'var(--back-light)'}">
|
|
|
- <view class="p-30 grid grid-cols-2 gap-28">
|
|
|
- <ie-image v-for="(item,i) in baseInfo.images" :key="i" :src="item.url" mode="aspectFill"
|
|
|
- :round="6" custom-class="w-335 h-200" @click="handlePreview(i)"/>
|
|
|
- </view>
|
|
|
- </scroll-view>
|
|
|
- </ie-popup>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
+ <!-- <uv-action-sheet ref="actionSheet" :title="baseInfo.tel" :actions="actions" safe-area-inset-bottom-->
|
|
|
+ <!-- close-on-click-overlay cancel-text="取消" @select="handleActionSelect"/>-->
|
|
|
+ <ie-popup ref="popup" :show-toolbar="false">
|
|
|
+ <template v-if="popupGroup">
|
|
|
+ <view class="h-90 flex justify-center items-center text-32 font-bold">{{ popupGroup.root.name }}</view>
|
|
|
+ <scroll-view scroll-y :style="{ maxHeight: '50vh', backgroundColor: 'var(--back-light)' }">
|
|
|
+ <uv-cell-group v-for="(g, i) in popupGroup.subGroups" :key="i" title="1">
|
|
|
+ <template #title>
|
|
|
+ <text class="text-24 text-fore-tip">{{ g.parent.name }}</text>
|
|
|
+ </template>
|
|
|
+ <uv-cell v-for="p in g.list" :title="p.name" custom-class="bg-white" />
|
|
|
+ </uv-cell-group>
|
|
|
+ </scroll-view>
|
|
|
+ </template>
|
|
|
+ </ie-popup>
|
|
|
+ <ie-popup ref="imagesPopup" :show-toolbar="false">
|
|
|
+ <view class="h-90 flex justify-center items-center text-32 font-bold">院校风采</view>
|
|
|
+ <scroll-view scroll-y :style="{ maxHeight: '50vh', backgroundColor: 'var(--back-light)' }">
|
|
|
+ <view class="p-30 grid grid-cols-2 gap-28">
|
|
|
+ <ie-image v-for="(item, i) in baseInfo.images" :key="i" :src="item.url" mode="aspectFill" :round="6"
|
|
|
+ custom-class="w-335 h-200" @click="handlePreview(i)" />
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </ie-popup>
|
|
|
+ </view>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import {MAJOR_TREE, UNIVERSITY_DETAIL} from "@/types/injectionSymbols";
|
|
|
-import {University, UniversityDetail, UniversityProfession} from "@/types/university";
|
|
|
-import {MajorItem} from "@/types/major";
|
|
|
+import { MAJOR_TREE, UNIVERSITY_DETAIL } from "@/types/injectionSymbols";
|
|
|
+import { University, UniversityDetail, UniversityProfession } from "@/types/university";
|
|
|
+import { MajorItem } from "@/types/major";
|
|
|
import UvReadMore from "@/uni_modules/uv-read-more/components/uv-read-more/uv-read-more.vue";
|
|
|
import UvActionSheet from "@/uni_modules/uv-action-sheet/components/uv-action-sheet/uv-action-sheet.vue";
|
|
|
import IePopup from "@/components/ie-popup/ie-popup.vue";
|
|
|
|
|
|
interface ActionItem {
|
|
|
- id: string;
|
|
|
- name: string;
|
|
|
- icon?: string;
|
|
|
- color?: string;
|
|
|
- iconColor?: string;
|
|
|
- disabled?: boolean;
|
|
|
+ id: string;
|
|
|
+ name: string;
|
|
|
+ icon?: string;
|
|
|
+ color?: string;
|
|
|
+ iconColor?: string;
|
|
|
+ disabled?: boolean;
|
|
|
}
|
|
|
|
|
|
interface ProfessionSubGroup {
|
|
|
- parent: MajorItem;
|
|
|
- list: UniversityProfession[];
|
|
|
+ parent: MajorItem;
|
|
|
+ list: UniversityProfession[];
|
|
|
}
|
|
|
|
|
|
interface ProfessionGroup {
|
|
|
- root: MajorItem;
|
|
|
- subGroups: ProfessionSubGroup[];
|
|
|
- count: number;
|
|
|
+ root: MajorItem;
|
|
|
+ subGroups: ProfessionSubGroup[];
|
|
|
+ count: number;
|
|
|
}
|
|
|
|
|
|
defineProps({
|
|
|
- loading: Boolean
|
|
|
+ loading: Boolean
|
|
|
})
|
|
|
|
|
|
const imagesPopup = ref<InstanceType<typeof IePopup>>()
|
|
|
@@ -109,159 +109,141 @@ const more = ref<InstanceType<typeof UvReadMore>>()
|
|
|
const actionSheet = ref<InstanceType<typeof UvActionSheet>>()
|
|
|
const buttonClass = "flex-1 py-16 rounded-lg flex justify-center items-center gap-8"
|
|
|
|
|
|
-const actions = computed(() => [{
|
|
|
- id: 'call',
|
|
|
- name: '打电话'
|
|
|
-}, {
|
|
|
- id: 'copy',
|
|
|
- name: '复制'
|
|
|
-}])
|
|
|
-
|
|
|
const popup = ref<InstanceType<typeof IePopup>>()
|
|
|
const popupGroup = ref<ProfessionGroup>()
|
|
|
const grouped = computed<ProfessionGroup[]>(() => {
|
|
|
- if (!majorTree.value || !professions.value) return [];
|
|
|
-
|
|
|
- // 预先构建三级节点到其路径的映射
|
|
|
- const nodePathCache = new Map<string, { root: MajorItem; parent: MajorItem }>();
|
|
|
-
|
|
|
- const buildPathCache = (node: MajorItem, root?: MajorItem, parent?: MajorItem) => {
|
|
|
- if (node.children?.length) {
|
|
|
- // 非叶子节点
|
|
|
- const newRoot = root || node;
|
|
|
- const isRoot = !root;
|
|
|
- const newParent = isRoot ? undefined : (parent || node);
|
|
|
+ if (!majorTree.value || !professions.value) return [];
|
|
|
|
|
|
- node.children.forEach(child => buildPathCache(child, newRoot, newParent));
|
|
|
- } else {
|
|
|
- // 叶子节点(三级节点)
|
|
|
- if (root && parent) {
|
|
|
- nodePathCache.set(node.code, {root, parent});
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
+ // 预先构建三级节点到其路径的映射
|
|
|
+ const nodePathCache = new Map<string, { root: MajorItem; parent: MajorItem }>();
|
|
|
|
|
|
- majorTree.value.forEach(node => buildPathCache(node));
|
|
|
+ const buildPathCache = (node: MajorItem, root?: MajorItem, parent?: MajorItem) => {
|
|
|
+ if (node.children?.length) {
|
|
|
+ // 非叶子节点
|
|
|
+ const newRoot = root || node;
|
|
|
+ const isRoot = !root;
|
|
|
+ const newParent = isRoot ? undefined : (parent || node);
|
|
|
|
|
|
- // 分组逻辑
|
|
|
- const rootMap = new Map<string, {
|
|
|
- root: MajorItem;
|
|
|
- parentMap: Map<string, {
|
|
|
- parent: MajorItem;
|
|
|
- list: UniversityProfession[];
|
|
|
- }>;
|
|
|
- count: number;
|
|
|
- }>();
|
|
|
-
|
|
|
- professions.value.forEach(profession => {
|
|
|
- const path = nodePathCache.get(profession.code);
|
|
|
- if (!path) return;
|
|
|
-
|
|
|
- const {root, parent} = path;
|
|
|
+ node.children.forEach(child => buildPathCache(child, newRoot, newParent));
|
|
|
+ } else {
|
|
|
+ // 叶子节点(三级节点)
|
|
|
+ if (root && parent) {
|
|
|
+ nodePathCache.set(node.code, { root, parent });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- let rootEntry = rootMap.get(root.code);
|
|
|
- if (!rootEntry) {
|
|
|
- rootEntry = {
|
|
|
- root,
|
|
|
- parentMap: new Map(),
|
|
|
- count: 0
|
|
|
- };
|
|
|
- rootMap.set(root.code, rootEntry);
|
|
|
- }
|
|
|
+ majorTree.value.forEach(node => buildPathCache(node));
|
|
|
|
|
|
- let parentEntry = rootEntry.parentMap.get(parent.code);
|
|
|
- if (!parentEntry) {
|
|
|
- parentEntry = {
|
|
|
- parent,
|
|
|
- list: []
|
|
|
- };
|
|
|
- rootEntry.parentMap.set(parent.code, parentEntry);
|
|
|
- }
|
|
|
+ // 分组逻辑
|
|
|
+ const rootMap = new Map<string, {
|
|
|
+ root: MajorItem;
|
|
|
+ parentMap: Map<string, {
|
|
|
+ parent: MajorItem;
|
|
|
+ list: UniversityProfession[];
|
|
|
+ }>;
|
|
|
+ count: number;
|
|
|
+ }>();
|
|
|
+
|
|
|
+ professions.value.forEach(profession => {
|
|
|
+ const path = nodePathCache.get(profession.code);
|
|
|
+ if (!path) return;
|
|
|
+
|
|
|
+ const { root, parent } = path;
|
|
|
+
|
|
|
+ let rootEntry = rootMap.get(root.code);
|
|
|
+ if (!rootEntry) {
|
|
|
+ rootEntry = {
|
|
|
+ root,
|
|
|
+ parentMap: new Map(),
|
|
|
+ count: 0
|
|
|
+ };
|
|
|
+ rootMap.set(root.code, rootEntry);
|
|
|
+ }
|
|
|
|
|
|
- parentEntry.list.push(profession);
|
|
|
- rootEntry.count++;
|
|
|
- });
|
|
|
+ let parentEntry = rootEntry.parentMap.get(parent.code);
|
|
|
+ if (!parentEntry) {
|
|
|
+ parentEntry = {
|
|
|
+ parent,
|
|
|
+ list: []
|
|
|
+ };
|
|
|
+ rootEntry.parentMap.set(parent.code, parentEntry);
|
|
|
+ }
|
|
|
|
|
|
- // 转换为最终格式
|
|
|
- return Array.from(rootMap.values())
|
|
|
- .map(({root, parentMap, count}) => ({
|
|
|
- root,
|
|
|
- subGroups: Array.from(parentMap.values()),
|
|
|
- count
|
|
|
- }))
|
|
|
+ parentEntry.list.push(profession);
|
|
|
+ rootEntry.count++;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 转换为最终格式
|
|
|
+ return Array.from(rootMap.values())
|
|
|
+ .map(({ root, parentMap, count }) => ({
|
|
|
+ root,
|
|
|
+ subGroups: Array.from(parentMap.values()),
|
|
|
+ count
|
|
|
+ }))
|
|
|
});
|
|
|
|
|
|
const handleWebsite = () => {
|
|
|
- if (baseInfo.value.webSite) {
|
|
|
- uni.$ie.openBrowser(baseInfo.value.webSite)
|
|
|
- } else {
|
|
|
- uni.$ie.showToast('未提供网址')
|
|
|
- }
|
|
|
+ if (baseInfo.value.webSite) {
|
|
|
+ uni.$ie.openBrowser(baseInfo.value.webSite)
|
|
|
+ } else {
|
|
|
+ uni.$ie.showToast('未提供网址')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const handleMore = () => {
|
|
|
+ imagesPopup.value?.open()
|
|
|
}
|
|
|
|
|
|
const handlePhone = () => {
|
|
|
- if (baseInfo.value.tel) {
|
|
|
- //UvActionSheet 在这里打开有异常,所以先使用粘贴板
|
|
|
- // actionSheet.value?.open()
|
|
|
- uni.setClipboardData({
|
|
|
- data: baseInfo.value.tel,
|
|
|
- showToast: false,
|
|
|
- success: () => {
|
|
|
- uni.showModal({
|
|
|
- title: '提示',
|
|
|
- content: '号码已复制',
|
|
|
- showCancel: false
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
+ const { zhaoBanDH } = baseInfo.value;
|
|
|
+ try {
|
|
|
+ if (zhaoBanDH) {
|
|
|
+ const parsedDH = JSON.parse(zhaoBanDH);
|
|
|
+ if (Array.isArray(parsedDH) && parsedDH.length)
|
|
|
+ uni.showActionSheet({
|
|
|
+ title: '招生电话',
|
|
|
+ itemList: parsedDH,
|
|
|
+ success: (res) => {
|
|
|
+ const index = res.tapIndex;
|
|
|
+ const tel = parsedDH[index];
|
|
|
+ uni.makePhoneCall({ phoneNumber: tel })
|
|
|
+ }
|
|
|
+ });
|
|
|
} else {
|
|
|
- uni.$ie.showToast('未提供电话')
|
|
|
+ throw new Error('未提供电话')
|
|
|
}
|
|
|
+ } catch (error) {
|
|
|
+ uni.$ie.showToast('未提供电话')
|
|
|
+ }
|
|
|
}
|
|
|
const handlePreview = (index: number) => {
|
|
|
- if (!baseInfo.value.images?.length) return
|
|
|
- // TODO: #1 uni.previewImage 与 ie-popup 在H5端有遮挡问题 #2 previewImage有些图片在微信端里加载不出来?见民政
|
|
|
- uni.previewImage({
|
|
|
- urls: baseInfo.value.images.map(i => i.url),
|
|
|
- current: index,
|
|
|
- indicator: "number"
|
|
|
- })
|
|
|
-}
|
|
|
-const handleActionSelect = (e: ActionItem) => {
|
|
|
- if (e.id == 'call') {
|
|
|
- uni.makePhoneCall({phoneNumber: baseInfo.value.tel})
|
|
|
- } else if (e.id == 'copy') {
|
|
|
- uni.setClipboardData({
|
|
|
- data: baseInfo.value.tel,
|
|
|
- success: () => {
|
|
|
- uni.showModal({
|
|
|
- title: '提示',
|
|
|
- content: '号码已复制',
|
|
|
- showCancel: false
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
+ if (!baseInfo.value.images?.length) return
|
|
|
+ uni.previewImage({
|
|
|
+ urls: baseInfo.value.images.map(i => i.url),
|
|
|
+ current: index,
|
|
|
+ indicator: "number"
|
|
|
+ })
|
|
|
}
|
|
|
+
|
|
|
const handleProfessionGroup = (g: ProfessionGroup) => {
|
|
|
- popupGroup.value = g
|
|
|
- popup.value?.open()
|
|
|
+ popupGroup.value = g
|
|
|
+ popup.value?.open()
|
|
|
}
|
|
|
|
|
|
watch(() => baseInfo.value.introduction, async (val) => {
|
|
|
- if (val) {
|
|
|
- await nextTick()
|
|
|
- more.value?.init()
|
|
|
- }
|
|
|
+ if (val) {
|
|
|
+ await nextTick()
|
|
|
+ more.value?.init()
|
|
|
+ }
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
::v-deep .uv-cell-group__title__text {
|
|
|
- font-size: 12px;
|
|
|
- color: #999999;
|
|
|
+ font-size: 12px;
|
|
|
+ color: #999999;
|
|
|
}
|
|
|
</style>
|
|
|
|
|
|
-<style lang="scss">
|
|
|
-</style>
|
|
|
+<style lang="scss"></style>
|