|
@@ -0,0 +1,233 @@
|
|
|
|
|
+<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 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" loading rows="3"/>
|
|
|
|
|
+ <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>
|
|
|
|
|
+ <!-- <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>
|
|
|
|
|
+ </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 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;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface ProfessionSubGroup {
|
|
|
|
|
+ parent: MajorItem;
|
|
|
|
|
+ list: UniversityProfession[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface ProfessionGroup {
|
|
|
|
|
+ root: MajorItem;
|
|
|
|
|
+ subGroups: ProfessionSubGroup[];
|
|
|
|
|
+ count: number;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+defineProps({
|
|
|
|
|
+ loading: Boolean
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const detail = inject(UNIVERSITY_DETAIL) || ref({} as UniversityDetail)
|
|
|
|
|
+const majorTree = inject(MAJOR_TREE) || ref([])
|
|
|
|
|
+const baseInfo = computed<University>(() => detail.value.baseInfo || {})
|
|
|
|
|
+const professions = computed<UniversityProfession[]>(() => detail.value.professions || [])
|
|
|
|
|
+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);
|
|
|
|
|
+
|
|
|
|
|
+ node.children.forEach(child => buildPathCache(child, newRoot, newParent));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 叶子节点(三级节点)
|
|
|
|
|
+ if (root && parent) {
|
|
|
|
|
+ nodePathCache.set(node.code, {root, parent});
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ majorTree.value.forEach(node => buildPathCache(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;
|
|
|
|
|
+
|
|
|
|
|
+ let rootEntry = rootMap.get(root.code);
|
|
|
|
|
+ if (!rootEntry) {
|
|
|
|
|
+ rootEntry = {
|
|
|
|
|
+ root,
|
|
|
|
|
+ parentMap: new Map(),
|
|
|
|
|
+ count: 0
|
|
|
|
|
+ };
|
|
|
|
|
+ rootMap.set(root.code, rootEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let parentEntry = rootEntry.parentMap.get(parent.code);
|
|
|
|
|
+ if (!parentEntry) {
|
|
|
|
|
+ parentEntry = {
|
|
|
|
|
+ parent,
|
|
|
|
|
+ list: []
|
|
|
|
|
+ };
|
|
|
|
|
+ rootEntry.parentMap.set(parent.code, parentEntry);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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('未提供网址')
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+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
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ uni.$ie.showToast('未提供电话')
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+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
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleProfessionGroup = (g: ProfessionGroup) => {
|
|
|
|
|
+ popupGroup.value = g
|
|
|
|
|
+ popup.value?.open()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+watch(() => baseInfo.value.introduction, async (val) => {
|
|
|
|
|
+ if (val) {
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ more.value?.init()
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+::v-deep .uv-cell-group__title__text {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999999;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|