feat: 调整逻辑和修改样式
parent
b7b3baf17b
commit
2902d26d02
|
|
@ -3,8 +3,22 @@ VITE_APP_PORT = 9000
|
|||
|
||||
VITE_UNI_APPID = 'H57F2ACE4'
|
||||
# VITE_WX_APPID = 'wxc48ad15d58a3e417' 六纬中考通
|
||||
# VITE_WX_APPID = 'wx4b925e36c17dd54a' 六纬裂变
|
||||
VITE_WX_APPID = 'wxc48ad15d58a3e417'
|
||||
# VITE_WX_APPID = 'wx4b925e36c17dd54a' 智能中专学校的
|
||||
# wx487ac749c0e10be5 深泉外国语的
|
||||
VITE_WX_APPID = 'wx487ac749c0e10be5'
|
||||
# VITE_WX_VIDEO_ID= 'sphju9MCfZetYHP' 智能中专学校的
|
||||
# VITE_WX_VIDEO_ID= 'spht0MPpWjxEKb4' 深泉外国语
|
||||
# spheuUaMulnlpHH 深泉外国语学院 认证的
|
||||
VITE_WX_VIDEO_ID= 'spht0MPpWjxEKb4'
|
||||
|
||||
# 微信小程序 AI
|
||||
# 智能中专学校的
|
||||
# VITE_CLOUD_ENV_ID= 'cloud1-d3g5q6bq61a240786',
|
||||
# VITE_CLOUD_BOT_ID= 'agent-wxai-4gl75um61026324f',
|
||||
|
||||
# 深泉外国语学院的
|
||||
VITE_CLOUD_ENV_ID='cloud1-d9g4odcaqf6a2114f'
|
||||
VITE_CLOUD_BOT_ID='agent-wxai-9gi3rts96061202f'
|
||||
|
||||
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
|
||||
# https://uniapp.dcloud.net.cn/collocation/manifest.html#h5-router
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ interface ImportMetaEnv {
|
|||
readonly VITE_SERVER_PORT: string
|
||||
/** 后台接口地址 */
|
||||
readonly VITE_SERVER_BASEURL: string
|
||||
/** 微信视频号 ID */
|
||||
readonly VITE_WX_VIDEO_ID: string
|
||||
/** H5是否需要代理 */
|
||||
readonly VITE_APP_PROXY_ENABLE: 'true' | 'false'
|
||||
/** H5是否需要代理,需要的话有个前缀 */
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@ import {
|
|||
generateMessageId,
|
||||
} from '../components/agent-config'
|
||||
import { fetchRecommendQuestions, streamMessage } from '../components/agent-service'
|
||||
import AgentHeader from '../components/AgentHeader.vue'
|
||||
import AgentInput from '../components/AgentInput.vue'
|
||||
import AgentMessage from '../components/AgentMessage.vue'
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
navigationBarTitleText: 'AI 助手',
|
||||
navigationBarTitleText: 'AI助手',
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -28,6 +29,8 @@ const aiStore = useAiStore()
|
|||
const paging = ref<any>(null)
|
||||
const messages = ref<AiMessage[]>([])
|
||||
const loading = ref(false)
|
||||
const initQuestionCapsules = computed(() => agentConfig.initQuestions.slice(0, 3))
|
||||
const shouldShowInitQuestionCapsules = computed(() => aiStore.messages.length === 0 && initQuestionCapsules.value.length > 0)
|
||||
|
||||
// 用于停止流:在新一轮发送时把上一轮的 controller 标记为 cancelled
|
||||
let cancelFlag = false
|
||||
|
|
@ -43,17 +46,18 @@ onLoad(() => {
|
|||
// #endif
|
||||
|
||||
aiStore.ensureThreadId()
|
||||
|
||||
})
|
||||
|
||||
// z-paging 聊天模式:第一页直接把 store 里的历史灌给它
|
||||
// 聊天模式数据约定:数组下标 0 = 视觉最底部(最新消息),所以要倒序传入
|
||||
function queryList() {
|
||||
if (aiStore.messages.length === 0) {
|
||||
const welcomeMsg = agentConfig.welcomeMsg?.trim()
|
||||
|
||||
if (aiStore.messages.length === 0 && welcomeMsg) {
|
||||
aiStore.addMessage({
|
||||
id: generateMessageId(),
|
||||
role: 'assistant',
|
||||
content: agentConfig.welcomeMsg,
|
||||
content: welcomeMsg,
|
||||
createdAt: Date.now(),
|
||||
})
|
||||
}
|
||||
|
|
@ -194,19 +198,6 @@ function handleClear() {
|
|||
|
||||
<template>
|
||||
<view class="h-screen w-full flex flex-col bg-[#F4F6FA]">
|
||||
<!-- 顶部 -->
|
||||
<view class="flex items-center justify-between bg-white px-[24rpx] py-[20rpx] shadow-[0_2rpx_8rpx_rgba(0,0,0,0.03)]">
|
||||
<view class="text-[30rpx] text-[#1F2329] font-500">
|
||||
{{ agentConfig.botName }}
|
||||
</view>
|
||||
<view
|
||||
class="rounded-[24rpx] bg-[#F5F6F8] px-[20rpx] py-[8rpx] text-[24rpx] text-[#666] active:opacity-60"
|
||||
@click="handleClear"
|
||||
>
|
||||
清空对话
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 消息列表(聊天模式) -->
|
||||
<view class="min-h-0 flex-1">
|
||||
<z-paging
|
||||
|
|
@ -224,22 +215,11 @@ function handleClear() {
|
|||
@query="queryList"
|
||||
>
|
||||
<!-- 首屏推荐问题 -->
|
||||
<template v-if="messages.length <= 1 && agentConfig.initQuestions.length" #top>
|
||||
<view class="flex flex-col gap-[16rpx] px-[24rpx] py-[20rpx]">
|
||||
<view class="text-[24rpx] text-[#999] leading-[1.4]">
|
||||
你可以试试这样问:
|
||||
</view>
|
||||
<view class="flex flex-wrap gap-[16rpx]">
|
||||
<view
|
||||
v-for="q in agentConfig.initQuestions"
|
||||
:key="q"
|
||||
class="border-1 border-[#1580FF] rounded-[24rpx] border-solid bg-white px-[20rpx] py-[10rpx] text-[24rpx] text-[#1580FF] active:opacity-60"
|
||||
@click="handleClickQuestion(q)"
|
||||
>
|
||||
{{ q }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<template v-if="shouldShowInitQuestionCapsules" #top>
|
||||
<agent-header
|
||||
:questions="initQuestionCapsules"
|
||||
@pick="handleClickQuestion"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
|
|
@ -254,13 +234,19 @@ function handleClear() {
|
|||
</template>
|
||||
|
||||
<template #bottom>
|
||||
<agent-input
|
||||
:placeholder="agentConfig.placeholder"
|
||||
:loading="loading"
|
||||
@send="handleSend"
|
||||
@stop="handleStop"
|
||||
@clear="handleClear"
|
||||
/>
|
||||
<view>
|
||||
<view class="text-[24rpx] text-[#999] text-center my-[16rpx]">AI建议仅供参考,请以官方政策为准</view>
|
||||
<agent-input
|
||||
:placeholder="agentConfig.placeholder"
|
||||
:loading="loading"
|
||||
@send="handleSend"
|
||||
@stop="handleStop"
|
||||
@clear="handleClear"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
<template #empty>
|
||||
<view />
|
||||
</template>
|
||||
</z-paging>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
<script lang="ts" setup>
|
||||
import {defaultAgentConfig} from "./agent-config"
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
questions?: string[]
|
||||
}>(), {
|
||||
questions: () => [],
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'pick', question: string): void
|
||||
}>()
|
||||
|
||||
function handlePick(question: string) {
|
||||
emit('pick', question)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="mt-[110rpx] box-border w-full px-[34rpx]">
|
||||
<view
|
||||
class="relative z-0 mb-[-108rpx] h-[210rpx] border-[2rpx] border-white rounded-[100rpx_26rpx_26rpx_26rpx] border-solid bg-[#B0E4FF] bg-opacity-80">
|
||||
<view
|
||||
class="agent-hello__glow absolute bottom-[46rpx] left-[12rpx] z-[2] h-[64rpx] w-[266rpx] rounded-[40rpx] blur-[20rpx]" />
|
||||
<image class="absolute bottom-[70rpx] h-[246rpx] w-[240rpx]"
|
||||
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/ai_zanyong.png" mode="aspectFit" />
|
||||
<view class="mr-[28rpx] mt-[30rpx] text-right text-[32rpx] text-[#000] font-500 leading-[1.4]">
|
||||
你好,我是你的{{ defaultAgentConfig.botName }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="relative z-[1] box-border border-[2rpx] border-white rounded-[26rpx] border-solid bg-white bg-opacity-70 px-[34rpx] pb-[36rpx] pt-[32rpx] backdrop-blur-[20rpx]">
|
||||
<view class="text-[28rpx] text-[#1F2329] leading-[1.5]">
|
||||
你好,我可以帮你解读济南中考政策、分数线和志愿填报问题。
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="props.questions.length" class="mt-[28rpx] flex flex-wrap gap-[16rpx]">
|
||||
<view v-for="item in props.questions" :key="item"
|
||||
class="border-[1rpx] border-[#E8E8E8] rounded-full border-solid bg-white px-[32rpx] py-[16rpx] text-[30rpx] text-[#000] active:opacity-60"
|
||||
@click="handlePick(item)">
|
||||
{{ item }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.agent-hello__glow {
|
||||
background: radial-gradient(ellipse at top, #1580ff, #fff);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -47,59 +47,52 @@ function handleClickClear() {
|
|||
<view class="flex flex-col bg-white pb-safe">
|
||||
<!-- 输入区 -->
|
||||
<view class="flex items-center gap-[16rpx] px-[24rpx] py-[18rpx]">
|
||||
<view class="min-h-[72rpx] flex flex-1 items-center rounded-[40rpx] bg-[#F5F6F8] px-[24rpx] py-[12rpx]">
|
||||
<textarea
|
||||
v-model="inputValue"
|
||||
class="max-h-[200rpx] w-full bg-transparent text-[28rpx] text-[#1F2329]"
|
||||
:placeholder="props.placeholder || '输入你的问题'"
|
||||
placeholder-style="color:#A6AFBD;font-size:28rpx;"
|
||||
auto-height
|
||||
:show-confirm-bar="false"
|
||||
:adjust-position="true"
|
||||
cursor-spacing="20"
|
||||
confirm-type="send"
|
||||
@confirm="handleSend"
|
||||
@focus="showActions = false"
|
||||
/>
|
||||
<view class="flex flex-1 items-center rounded-full bg-[#F5F6F8] p-[16rpx]">
|
||||
<textarea v-model="inputValue" class="max-h-[200rpx] w-full bg-transparent text-[28rpx] text-[#1F2329] ml-[14rpx]"
|
||||
:placeholder="props.placeholder || '有中考问题,直接问我!'" placeholder-style="color:#A6AFBD;font-size:28rpx;"
|
||||
auto-height :show-confirm-bar="false" :adjust-position="true" :cursor-spacing="20" confirm-type="send"
|
||||
@confirm="handleSend" @focus="showActions = false" />
|
||||
<!-- 发送 / 停止 / 展开更多 -->
|
||||
<view v-if="!props.loading && !inputValue.trim()"
|
||||
class="h-[52rpx] w-[52rpx] flex items-center justify-center rounded-full "
|
||||
:class="showActions ? 'rotate-45' : ''" @click="toggleActions">
|
||||
<image
|
||||
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/ai_tianjia.png"
|
||||
mode="scaleToFill"
|
||||
class="w-[52rpx] h-[52rpx]"
|
||||
/>
|
||||
</view>
|
||||
<view v-else-if="!props.loading"
|
||||
class="h-[52rpx] w-[52rpx] flex items-center justify-center rounded-[36rpx] "
|
||||
@click="handleSend">
|
||||
<image
|
||||
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/ai_fasong.png"
|
||||
mode="scaleToFill"
|
||||
class="h-[52rpx] w-[52rpx]"
|
||||
/>
|
||||
</view>
|
||||
<view v-else
|
||||
class="h-[52rpx] w-[52rpx] flex items-center justify-center"
|
||||
@click="emit('stop')">
|
||||
<image
|
||||
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/ai_zanting.png"
|
||||
mode="scaleToFill"
|
||||
class="h-[52rpx] w-[52rpx]"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 发送 / 停止 / 展开更多 -->
|
||||
<view
|
||||
v-if="!props.loading && !inputValue.trim()"
|
||||
class="h-[72rpx] w-[72rpx] flex items-center justify-center rounded-full bg-[#F5F6F8] text-[40rpx] text-[#666] transition active:opacity-60"
|
||||
:class="showActions ? 'rotate-45' : ''"
|
||||
@click="toggleActions"
|
||||
>
|
||||
+
|
||||
</view>
|
||||
<view
|
||||
v-else-if="!props.loading"
|
||||
class="h-[72rpx] w-[120rpx] flex items-center justify-center rounded-[36rpx] bg-[#1580FF] text-[28rpx] text-white font-500 transition active:opacity-80"
|
||||
@click="handleSend"
|
||||
>
|
||||
发送
|
||||
</view>
|
||||
<view
|
||||
v-else
|
||||
class="h-[72rpx] w-[120rpx] flex items-center justify-center border-1 border-[#1580FF] rounded-[36rpx] border-solid bg-white text-[28rpx] text-[#1580FF] font-500 active:opacity-80"
|
||||
@click="emit('stop')"
|
||||
>
|
||||
停止
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 展开的操作面板 -->
|
||||
<view
|
||||
v-if="showActions"
|
||||
class="grid grid-cols-4 gap-[16rpx] px-[24rpx] pb-[24rpx] pt-[12rpx]"
|
||||
>
|
||||
<view
|
||||
class="flex flex-col items-center gap-[8rpx] rounded-[16rpx] bg-[#F5F6F8] py-[24rpx] active:opacity-60"
|
||||
@click="handleClickClear"
|
||||
>
|
||||
<view class="h-[64rpx] w-[64rpx] flex items-center justify-center rounded-full bg-[#FFE4E1] text-[32rpx] text-[#EB5241]">
|
||||
清
|
||||
</view>
|
||||
<view v-if="showActions" class="flex gap-[16rpx] px-[24rpx] pb-[24rpx] pt-[12rpx]">
|
||||
<view class="flex items-center justify-center p-[20rpx] bg-[#F5F6F8] active:opacity-60"
|
||||
@click="handleClickClear">
|
||||
<image
|
||||
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/ai_qingkong.png"
|
||||
mode="scaleToFill"
|
||||
class="h-[52rpx] w-[52rpx]"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -13,32 +13,22 @@ defineEmits<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<view
|
||||
class="cell-flip flex px-[24rpx] py-[16rpx]"
|
||||
:class="props.message.role === 'user' ? 'justify-end' : 'justify-start'"
|
||||
>
|
||||
<view class="cell-flip flex px-[24rpx] py-[16rpx]"
|
||||
:class="props.message.role === 'user' ? 'justify-end' : 'justify-start'">
|
||||
<!-- AI 头像 -->
|
||||
<view
|
||||
v-if="props.message.role !== 'user'"
|
||||
class="mr-[16rpx] h-[64rpx] w-[64rpx] flex shrink-0 items-center justify-center rounded-full bg-[#1580FF] text-[24rpx] text-white"
|
||||
>
|
||||
<!-- <view v-if="props.message.role !== 'user'"
|
||||
class="mr-[16rpx] h-[64rpx] w-[64rpx] flex shrink-0 items-center justify-center rounded-full bg-[#1580FF] text-[24rpx] text-white">
|
||||
AI
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<view class="max-w-[78%] flex flex-col gap-[8rpx]">
|
||||
<view
|
||||
class="break-all rounded-[16rpx] px-[24rpx] py-[18rpx] text-[28rpx] leading-[1.6]"
|
||||
:class="props.message.role === 'user'
|
||||
? 'bg-[#1580FF] text-white rounded-tr-[4rpx]'
|
||||
: 'rounded-tl-[4rpx] bg-white text-[#1F2329] shadow-[0_2rpx_12rpx_rgba(0,0,0,0.04)]'"
|
||||
>
|
||||
<view class="max-w-[90%] flex flex-col gap-[8rpx]">
|
||||
<view class="break-all rounded-[16rpx] px-[24rpx] py-[18rpx] text-[28rpx] leading-[1.6]" :class="props.message.role === 'user'
|
||||
? 'bg-[#1580FF] text-white rounded-tr-[4rpx]'
|
||||
: 'rounded-tl-[4rpx] bg-white text-[#1F2329] shadow-[0_2rpx_12rpx_rgba(0,0,0,0.04)]'">
|
||||
<!-- AI 回复用 markdown 渲染(仅微信小程序) -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<markdown-preview
|
||||
v-if="props.message.role !== 'user'"
|
||||
:markdown="props.message.content || (props.message.pending ? '' : '')"
|
||||
:font-size="28"
|
||||
/>
|
||||
<markdown-preview v-if="props.message.role !== 'user'"
|
||||
:markdown="props.message.content || (props.message.pending ? '' : '')" :font-size="28" />
|
||||
<text v-else class="select-text">
|
||||
{{ props.message.content }}
|
||||
</text>
|
||||
|
|
@ -56,37 +46,28 @@ defineEmits<{
|
|||
</text>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="props.message.error"
|
||||
class="self-start text-[24rpx] text-[#EB5241] leading-[1.4] active:opacity-60"
|
||||
@click="$emit('retry', props.message.id)"
|
||||
>
|
||||
<view v-if="props.message.error" class="self-start text-[24rpx] text-[#EB5241] leading-[1.4] active:opacity-60"
|
||||
@click="$emit('retry', props.message.id)">
|
||||
生成失败,点击重试
|
||||
</view>
|
||||
|
||||
<!-- 追问建议(仅 AI 消息且生成结束后展示) -->
|
||||
<view
|
||||
v-if="props.message.role === 'assistant' && !props.message.pending && props.message.recommendQuestions && props.message.recommendQuestions.length"
|
||||
class="mt-[8rpx] flex flex-col gap-[12rpx]"
|
||||
>
|
||||
<view
|
||||
v-for="q in props.message.recommendQuestions"
|
||||
:key="q"
|
||||
class="mt-[8rpx] flex flex-col gap-[12rpx]">
|
||||
<view v-for="q in props.message.recommendQuestions" :key="q"
|
||||
class="self-start border-1 border-[#1580FF] rounded-[24rpx] border-solid bg-white px-[20rpx] py-[10rpx] text-[24rpx] text-[#1580FF] active:opacity-60"
|
||||
@click="$emit('pickRecommend', q)"
|
||||
>
|
||||
@click="$emit('pickRecommend', q)">
|
||||
{{ q }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户头像 -->
|
||||
<view
|
||||
v-if="props.message.role === 'user'"
|
||||
class="ml-[16rpx] h-[64rpx] w-[64rpx] flex shrink-0 items-center justify-center rounded-full bg-[#FFA94D] text-[28rpx] text-white"
|
||||
>
|
||||
<!-- <view v-if="props.message.role === 'user'"
|
||||
class="ml-[16rpx] h-[64rpx] w-[64rpx] flex shrink-0 items-center justify-center rounded-full bg-[#FFA94D] text-[28rpx] text-white">
|
||||
U
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -101,11 +82,13 @@ defineEmits<{
|
|||
}
|
||||
|
||||
@keyframes pulse {
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
40% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ export interface AgentConfig {
|
|||
cloudEnvId: string
|
||||
/** Agent ID,对应 wx.cloud.extend.AI.bot.sendMessage 的 botId(chatMode=bot 必填) */
|
||||
botId: string
|
||||
/** 首屏欢迎语 */
|
||||
welcomeMsg: string
|
||||
/** 首屏欢迎语;不设置或留空时不插入欢迎消息 */
|
||||
welcomeMsg?: string
|
||||
/** 输入框 placeholder */
|
||||
placeholder: string
|
||||
/** 显示在 AI 头像位置的图标 / 文字 */
|
||||
|
|
@ -31,15 +31,14 @@ export interface ModelConfig {
|
|||
|
||||
export const defaultAgentConfig: AgentConfig = {
|
||||
chatMode: 'bot',
|
||||
cloudEnvId: 'cloud1-d3g5q6bq61a240786',
|
||||
botId: 'agent-wxai-4gl75um61026324f',
|
||||
welcomeMsg: '你好,我是 AI 助手,有什么可以帮你?',
|
||||
placeholder: '输入你的问题',
|
||||
botName: 'AI 助手',
|
||||
cloudEnvId: import.meta.env.VITE_CLOUD_ENV_ID,
|
||||
botId: import.meta.env.VITE_CLOUD_BOT_ID,
|
||||
placeholder: '有中考问题,直接问我!',
|
||||
botName: '中考小助手',
|
||||
initQuestions: [
|
||||
'帮我介绍一下志愿填报流程',
|
||||
'中考分数线怎么查?',
|
||||
'推荐几所适合的高中',
|
||||
'济南500分还能上哪些高中?',
|
||||
'第二批志愿怎么填最稳',
|
||||
'指标生和统招生区别',
|
||||
],
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +63,6 @@ export const defaultModelAgentConfig: AgentConfig = {
|
|||
...defaultAgentConfig,
|
||||
chatMode: 'bot',
|
||||
botName: 'AI 模型助手',
|
||||
welcomeMsg: '你好,我是 AI 模型助手,可以直接用大模型和你对话。',
|
||||
}
|
||||
|
||||
export function generateMessageId() {
|
||||
|
|
|
|||
|
|
@ -51,9 +51,15 @@ export interface SendMessageOptions {
|
|||
export async function streamMessage(options: SendMessageOptions): Promise<string> {
|
||||
if (options.chatMode === 'model') {
|
||||
return streamModel(options)
|
||||
}else{
|
||||
return streamBot(options)
|
||||
}
|
||||
|
||||
return streamBot(options)
|
||||
}
|
||||
|
||||
function isValidHistoryMessage(message: AiMessage) {
|
||||
return !message.error
|
||||
&& (message.role === 'user' || message.role === 'assistant')
|
||||
&& message.content.trim().length > 0
|
||||
}
|
||||
|
||||
// ---------- bot 模式:wx.cloud.extend.AI.bot.sendMessage ----------
|
||||
|
|
@ -67,7 +73,7 @@ async function streamBot(options: SendMessageOptions): Promise<string> {
|
|||
|
||||
const messages = [
|
||||
...history
|
||||
.filter(m => !m.error && (m.role === 'user' || m.role === 'assistant'))
|
||||
.filter(isValidHistoryMessage)
|
||||
.map(m => ({ id: m.id, role: m.role, content: m.content })),
|
||||
{
|
||||
id: generateMessageId(),
|
||||
|
|
@ -133,7 +139,7 @@ async function streamModel(options: SendMessageOptions): Promise<string> {
|
|||
|
||||
const messages = [
|
||||
...history
|
||||
.filter(m => !m.error && (m.role === 'user' || m.role === 'assistant'))
|
||||
.filter(isValidHistoryMessage)
|
||||
.map(m => ({ role: m.role, content: m.content })),
|
||||
{ role: 'user', content: prompt },
|
||||
]
|
||||
|
|
@ -141,7 +147,7 @@ async function streamModel(options: SendMessageOptions): Promise<string> {
|
|||
// #ifdef MP-WEIXIN
|
||||
const ai = wx.cloud.extend.AI
|
||||
const aiModel = ai.createModel(modelProvider)
|
||||
console.log("aimodel创建",aiModel);
|
||||
console.log('aimodel创建', aiModel)
|
||||
|
||||
const res = await aiModel.streamText({
|
||||
data: {
|
||||
|
|
@ -210,7 +216,9 @@ export async function fetchRecommendQuestions(options: RecommendOptions): Promis
|
|||
const res = await wx.cloud.extend.AI.bot.getRecommendQuestions({
|
||||
data: {
|
||||
botId,
|
||||
history: lastPair.map(item => ({ role: item.role, content: item.content })),
|
||||
history: lastPair
|
||||
.filter(item => item.content.trim().length > 0)
|
||||
.map(item => ({ role: item.role, content: item.content })),
|
||||
msg: prompt,
|
||||
agentSetting: '',
|
||||
introduction: '',
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
<template>
|
||||
<view class="flex items-center bg-gray-100 rounded-full px-[30rpx] py-[20rpx]" :class="rootClass" :style="rootStyle">
|
||||
<view class="flex items-center bg-gray-100 rounded-full" :class="rootClass" :style="rootStyle">
|
||||
|
||||
<view class="w-[36rpx] h-[36rpx] mr-2 flex items-center">
|
||||
<image :src="searchIconUrl" class="w-[36rpx] h-[36rpx]" mode="aspectFit" />
|
||||
<view class="w-[32rpx] h-[32rpx] mr-2 flex items-center">
|
||||
<image :src="searchIconUrl" class="w-[32rpx] h-[32rpx]" mode="aspectFit" />
|
||||
</view>
|
||||
|
||||
<input :value="searchInnerText" type="text" :placeholder="placeholder"
|
||||
<input :value="searchInnerText" :type="inputType" :placeholder="placeholder"
|
||||
class="flex-1 bg-transparent text-sm text-gray-800 outline-none text-start" :class="inputClass" @input="handleInput" @confirm="handleChange" @blur="handleChange"/>
|
||||
|
||||
<view class="w-5 h-5 ml-2 flex items-center" v-if="clearIcon">
|
||||
<image v-if="searchInnerText" src="" class="w-5 h-5" mode="aspectFit" @tap="clearInput" />
|
||||
</view>
|
||||
|
||||
<view v-if="searchIcon" class="text-[30rpx] px-[48rpx] py-[12rpx] bg-[#1580FF] text-[#fff] rounded-full" @click="handleSubmit">查询</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
|
@ -44,9 +45,17 @@ const props = defineProps({
|
|||
type:String,
|
||||
default:''
|
||||
},
|
||||
inputType:{
|
||||
type:String,
|
||||
default:'text'
|
||||
},
|
||||
clearIcon:{
|
||||
type:Boolean,
|
||||
default: true
|
||||
},
|
||||
searchIcon:{
|
||||
type:Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -76,4 +85,8 @@ const handleChange = (event) => {
|
|||
emit('update:searchText', event.detail.value);
|
||||
emit("complete",event.detail.value)
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
emit("submit",searchInnerText.value)
|
||||
}
|
||||
</script>
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
<script lang="ts" setup>
|
||||
import { systemInfo } from '@/utils/systemInfo'
|
||||
import MxRadioGroup from "@/pages-sub/components/radio/index.vue?async"
|
||||
import { getAreaList, getHistoryYearList, getBatchLine } from "@/service"
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
definePage({
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
excludeLoginPath: false,
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
definePage({
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
transparentTitle: 'always',
|
||||
navigationBarTitleText: ''
|
||||
},
|
||||
excludeLoginPath: false,
|
||||
})
|
||||
// #endif
|
||||
|
||||
const handleBack = () => {
|
||||
uni.navigateBack({ delta: 1 })
|
||||
}
|
||||
|
||||
const areaList = ref<any[]>([])
|
||||
|
||||
|
||||
|
||||
|
||||
const searchParams = ref({
|
||||
keyword: '',
|
||||
region: null,
|
||||
nature: null,
|
||||
natureLabel: "",
|
||||
year: null
|
||||
})
|
||||
|
||||
const visibleFlag1 = ref(false)
|
||||
const visibleFlag3 = ref(false)
|
||||
|
||||
const partialData = ref<any[]>([])
|
||||
const yearList = ref<any[]>([])
|
||||
|
||||
const handleChange = () => {
|
||||
visibleFlag1.value = false;
|
||||
visibleFlag3.value = false;
|
||||
getBatchLine({
|
||||
query: {
|
||||
Area: searchParams.value.region,
|
||||
Year: searchParams.value.year
|
||||
}
|
||||
}).then(resp => {
|
||||
partialData.value = resp.result.items
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
|
||||
Promise.all([
|
||||
getAreaList().then(resp => {
|
||||
if (resp.code === 200) {
|
||||
areaList.value = [{ value: '', label: '不限' }, ...resp.result]
|
||||
}
|
||||
}),
|
||||
getHistoryYearList().then(resp => {
|
||||
if (resp.code === 200) {
|
||||
yearList.value = [...resp.result]
|
||||
searchParams.value.year = yearList.value[yearList.value.length - 1]?.value
|
||||
}
|
||||
})
|
||||
]).then(() => {
|
||||
handleChange()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="gradient-custom flex flex-col h-screen">
|
||||
<sar-navbar title="批次线" :show-back="true" @back="handleBack"
|
||||
:root-style="{ '--sar-navbar-bg': `rgba(255, 255, 255, 0)`, 'padding-top': `${systemInfo?.statusBarHeight}px`, '--sar-navbar-item-color': 'black' }">
|
||||
</sar-navbar>
|
||||
<sar-dropdown
|
||||
root-style="--sar-dropdown-placeholder-color:#666;--sar-dropdown-value-font-size:28rpx;--sar-dropdown-option-active-color:#1580FF;--sar-dropdown-box-shadow:0rpx 6rpx 8rpx 0rpx rgba(0,0,0,0.04);">
|
||||
<sar-dropdown-item v-model:visible="visibleFlag1" :title="searchParams.region || '区域'">
|
||||
<mx-radio-group v-model:value="searchParams.region" :options="areaList" label-key="label" value-key="value"
|
||||
custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid" @change="handleChange" />
|
||||
</sar-dropdown-item>
|
||||
<sar-dropdown-item v-model:visible="visibleFlag3" v-model="searchParams.year" :title="searchParams.year || '年份'">
|
||||
<mx-radio-group v-model:value="searchParams.year" :options="yearList" label-key="label" value-key="value"
|
||||
custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]" @change="handleChange" />
|
||||
</sar-dropdown-item>
|
||||
</sar-dropdown>
|
||||
|
||||
<scroll-view :scroll-y="true" class="flex-1 bg-[#f8f8f8]">
|
||||
<view class="p-[30rpx]">
|
||||
<view class="flex justify-between gap-[40rpx] bg-white rounded-[16rpx] mb-[20rpx] p-[30rpx]"
|
||||
v-for="(val, index) in partialData" :key="index">
|
||||
<view class="max-w-[502rpx]">
|
||||
<view class="text-[30rpx] text-wrap">{{ val.batch }}</view>
|
||||
<view class="flex flex-wrap gap-[10rpx] mt-[10rpx]">
|
||||
<view class="rounded-[8rpx] bg-[#F8F8F8] px-[10rpx] py-[4rpx] text-[24rpx] text-[#666] w-max">{{
|
||||
val.area }}</view>
|
||||
<view class="rounded-[8rpx] bg-[#F8F8F8] px-[10rpx] py-[4rpx] text-[24rpx] text-[#666] w-max">{{ val.year
|
||||
}}</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex text-[#1E40AF] items-center min-w-[100rpx]">
|
||||
<view class="text-[40rpx] font-[DinBold]">
|
||||
{{ val.score }}
|
||||
</view>
|
||||
<view class="text-[24rpx]">分</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -109,7 +109,7 @@ const handleComplete = () => {
|
|||
:root-style="{ '--sar-navbar-bg': `rgba(255, 255, 255, 0)`, '--sar-navbar-item-color': 'black', '--sar-navbar-title-max-width': '100%' }">
|
||||
<template #title>
|
||||
<view class="w-[448rpx] ml-[90rpx]">
|
||||
<mx-search v-model:searchText="searchParams.keyword" placeholder="输入高中名称" @complete="handleComplete"/>
|
||||
<mx-search root-class="pl-[30rpx] py-[20rpx]" v-model:searchText="searchParams.keyword" placeholder="输入高中名称" @complete="handleComplete"/>
|
||||
</view>
|
||||
</template>
|
||||
</sar-navbar>
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ const handleComplete = () => {
|
|||
<sar-navbar title="中考资讯" :show-back="true" @back="handleBack"
|
||||
:root-style="{ '--sar-navbar-bg': `rgba(255, 255, 255, 0)`, 'padding-top': `${systemInfo?.statusBarHeight}px`, '--sar-navbar-item-color': 'black' }">
|
||||
</sar-navbar>
|
||||
<mx-search v-model:searchText="searchParams.keyword" rootStyle="margin: 16rpx 30rpx 0;" @complete="handleComplete"/>
|
||||
<mx-search v-model:searchText="searchParams.keyword" rootStyle="margin: 16rpx 30rpx 0;" root-class="pl-[30rpx] py-[20rpx]" @complete="handleComplete"/>
|
||||
<mx-tabs :tabsList="tabs" rootClass="shadow-md mb-[20rpx]" @tab-change="handleChange" />
|
||||
</view>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts" setup>
|
||||
import MxRadioGroup from '@/pages-sub/components/radio/index.vue?async'
|
||||
import MxSearch from '@/pages-sub/components/search/index.vue?async'
|
||||
import { getAreaList, getHistoryYearList, getSchoolHistoricalScores, getSchoolNature } from '@/service'
|
||||
import { systemInfo } from '@/utils/systemInfo'
|
||||
import MxSearch from "@/pages-sub/components/search/index.vue?async"
|
||||
import MxRadioGroup from "@/pages-sub/components/radio/index.vue?async"
|
||||
import { getAreaList, getHistoryYearList, getSchoolHistoricalScores, getSchoolNature } from "@/service"
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
definePage({
|
||||
|
|
@ -18,106 +18,107 @@ definePage({
|
|||
style: {
|
||||
navigationStyle: 'custom',
|
||||
transparentTitle: 'always',
|
||||
navigationBarTitleText: ''
|
||||
navigationBarTitleText: '',
|
||||
},
|
||||
excludeLoginPath: false,
|
||||
})
|
||||
// #endif
|
||||
|
||||
const handleBack = () => {
|
||||
function handleBack() {
|
||||
uni.navigateBack({ delta: 1 })
|
||||
}
|
||||
|
||||
const areaList = ref<any[]>([])
|
||||
const natureList = ref<any[]>()
|
||||
|
||||
|
||||
|
||||
|
||||
const searchParams = ref({
|
||||
keyword: '',
|
||||
region: null,
|
||||
nature: null,
|
||||
natureLabel: "",
|
||||
year: null
|
||||
natureLabel: '',
|
||||
year: null,
|
||||
})
|
||||
|
||||
const partialColumns = [
|
||||
{
|
||||
title: '院校名称',
|
||||
prop: 'schoolName',
|
||||
width: '32%',
|
||||
align: "left"
|
||||
},
|
||||
{
|
||||
title: '所在区',
|
||||
prop: 'region',
|
||||
width: '16%',
|
||||
align: "center"
|
||||
|
||||
},
|
||||
{
|
||||
title: '统招位次',
|
||||
prop: 'ranking',
|
||||
width: '18%',
|
||||
align: "center"
|
||||
},
|
||||
{
|
||||
title: '办学性质',
|
||||
prop: 'schoolNature',
|
||||
width: '18%',
|
||||
align: "center"
|
||||
},
|
||||
{
|
||||
title: '分数',
|
||||
prop: 'admissionScore',
|
||||
width: '15%',
|
||||
align: "center"
|
||||
},
|
||||
] as const
|
||||
|
||||
const visibleFlag1 = ref(false)
|
||||
const visibleFlag2 = ref(false)
|
||||
const visibleFlag3 = ref(false)
|
||||
|
||||
const partialData = ref<any[]>([])
|
||||
interface HistoricalScore {
|
||||
schoolName?: string
|
||||
region?: string
|
||||
schoolNature?: string
|
||||
tags?: string
|
||||
ranking?: number | string | null
|
||||
admissionScore?: number | string | null
|
||||
}
|
||||
|
||||
interface PagingRef {
|
||||
reload: () => void
|
||||
complete: (data: HistoricalScore[] | false) => void
|
||||
}
|
||||
|
||||
const paging = ref<PagingRef | null>(null)
|
||||
const partialData = ref<HistoricalScore[]>([])
|
||||
const yearList = ref<any[]>([])
|
||||
|
||||
const handleChange = () => {
|
||||
visibleFlag1.value = false;
|
||||
visibleFlag2.value = false;
|
||||
visibleFlag3.value = false;
|
||||
function handleChange() {
|
||||
visibleFlag1.value = false
|
||||
visibleFlag2.value = false
|
||||
visibleFlag3.value = false
|
||||
paging.value?.reload()
|
||||
}
|
||||
|
||||
function queryList(page: number, _pageSize: number) {
|
||||
if (page > 1) {
|
||||
paging.value?.complete([])
|
||||
return
|
||||
}
|
||||
|
||||
getSchoolHistoricalScores({
|
||||
query: {
|
||||
SchoolName: searchParams.value.keyword,
|
||||
Region: searchParams.value.region,
|
||||
SchoolType: searchParams.value.nature,
|
||||
Year: searchParams.value.year
|
||||
Year: searchParams.value.year,
|
||||
},
|
||||
}).then((resp) => {
|
||||
if (resp.code === 200) {
|
||||
paging.value?.complete(Array.isArray(resp.result) ? resp.result : [])
|
||||
}
|
||||
}).then(resp => {
|
||||
partialData.value = resp.result
|
||||
else {
|
||||
paging.value?.complete(false)
|
||||
}
|
||||
}).catch(() => {
|
||||
paging.value?.complete(false)
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
function virtualListChange(_vList: HistoricalScore[]) {
|
||||
partialData.value = _vList
|
||||
}
|
||||
|
||||
function getTagList(tags?: string) {
|
||||
return (tags || '').split(/[\s,,、]+/).filter(Boolean).slice(0, 2)
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
Promise.all([
|
||||
getAreaList().then(resp => {
|
||||
getAreaList().then((resp) => {
|
||||
if (resp.code === 200) {
|
||||
areaList.value = [{ value: '', label: '不限' }, ...resp.result]
|
||||
}
|
||||
}),
|
||||
getSchoolNature().then(resp => {
|
||||
getSchoolNature().then((resp) => {
|
||||
if (resp.code === 200) {
|
||||
natureList.value = [{ value: '', label: '不限' }, ...resp.result]
|
||||
}
|
||||
}),
|
||||
getHistoryYearList().then(resp => {
|
||||
getHistoryYearList().then((resp) => {
|
||||
if (resp.code === 200) {
|
||||
yearList.value = [...resp.result]
|
||||
searchParams.value.year = yearList.value[yearList.value.length - 1]?.value
|
||||
}
|
||||
})
|
||||
}),
|
||||
]).then(() => {
|
||||
handleChange()
|
||||
})
|
||||
|
|
@ -125,61 +126,103 @@ onShow(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<view class="gradient-custom flex flex-col h-screen">
|
||||
<sar-navbar title="历年分数" :show-back="true" @back="handleBack"
|
||||
:root-style="{ '--sar-navbar-bg': `rgba(255, 255, 255, 0)`, 'padding-top': `${systemInfo?.statusBarHeight}px`, '--sar-navbar-item-color': 'black' }">
|
||||
</sar-navbar>
|
||||
<mx-search v-model:searchText="searchParams.keyword" rootStyle="margin: 16rpx 30rpx 0;" @complete="handleChange" />
|
||||
<sar-dropdown
|
||||
root-style="--sar-dropdown-placeholder-color:#666;--sar-dropdown-value-font-size:28rpx;--sar-dropdown-option-active-color:#1580FF;--sar-dropdown-box-shadow:0rpx 6rpx 8rpx 0rpx rgba(0,0,0,0.04);">
|
||||
<sar-dropdown-item v-model:visible="visibleFlag1" :title="searchParams.region || '区域'">
|
||||
<mx-radio-group v-model:value="searchParams.region" :options="areaList" label-key="label" value-key="value"
|
||||
custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid" @change="handleChange" />
|
||||
</sar-dropdown-item>
|
||||
<sar-dropdown-item v-model:visible="visibleFlag2" :title="searchParams.natureLabel || '办学性质'">
|
||||
<mx-radio-group v-model:value="searchParams.nature" v-model:label="searchParams.natureLabel"
|
||||
:options="natureList" label-key="label" value-key="value" custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid" @change="handleChange" />
|
||||
</sar-dropdown-item>
|
||||
<sar-dropdown-item v-model:visible="visibleFlag3" v-model="searchParams.year" :title="searchParams.year || '年份'">
|
||||
<mx-radio-group v-model:value="searchParams.year" :options="yearList" label-key="label" value-key="value"
|
||||
custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]" @change="handleChange" />
|
||||
</sar-dropdown-item>
|
||||
</sar-dropdown>
|
||||
<z-paging
|
||||
ref="paging" use-virtual-list :force-close-inner-list="true" cell-height-mode="dynamic"
|
||||
:auto="false" :auto-show-system-loading="false" :safe-area-inset-bottom="true" :paging-style="{ backgroundColor: '#f8f8f8' }"
|
||||
@virtual-list-change="virtualListChange" @query="queryList"
|
||||
>
|
||||
<template #top>
|
||||
<view class="gradient-custom" :style="{ 'padding-top': `${systemInfo?.statusBarHeight}px` }">
|
||||
<sar-navbar
|
||||
title="历年分数" :show-back="true" :root-style="{ '--sar-navbar-bg': `rgba(255, 255, 255, 0)`, '--sar-navbar-item-color': 'black', '--sar-navbar-title-max-width': '100%' }"
|
||||
@back="handleBack"
|
||||
>
|
||||
<template #title>
|
||||
<view class="ml-[90rpx] w-[448rpx]">
|
||||
<mx-search v-model:search-text="searchParams.keyword" root-class="py-[20rpx] pl-[30rpx]" @complete="handleChange" />
|
||||
</view>
|
||||
</template>
|
||||
</sar-navbar>
|
||||
|
||||
<scroll-view :scroll-y="true" class="flex-1">
|
||||
<view class="p-[30rpx]">
|
||||
<sar-table bordered>
|
||||
<sar-table-row>
|
||||
<sar-table-cell v-for="item in partialColumns" :key="item.prop" bold :width="item.width">
|
||||
<view
|
||||
:class="`${item.align === 'center' ? '' : 'pl-[20rpx]'} py-[13rpx] text-[#333] text-[24rpx] font-500 bg-[#F3F4F8]`"
|
||||
:style="{ 'text-align': item.align }">{{ item.title }}</view>
|
||||
</sar-table-cell>
|
||||
</sar-table-row>
|
||||
<sar-table-row v-for="record in partialData" :key="record.id">
|
||||
<sar-table-cell v-for="item in partialColumns" :key="item.prop" :width="item.width">
|
||||
<view :class="`${item.align === 'center' ? '' : 'pl-[20rpx]'} text-[24rpx] text-[#333] py-[13rpx]`"
|
||||
:style="{ 'text-align': item.align }">{{ record[item.prop] }}</view>
|
||||
</sar-table-cell>
|
||||
</sar-table-row>
|
||||
<sar-table-row v-if="partialData.length === 0">
|
||||
<sar-table-cell :colspan="partialColumns.length">
|
||||
<view class="text-center text-[24rpx] text-[#999] py-[20rpx]">暂无数据</view>
|
||||
</sar-table-cell>
|
||||
</sar-table-row>
|
||||
</sar-table>
|
||||
<sar-dropdown
|
||||
root-style="--sar-dropdown-placeholder-color:#666;--sar-dropdown-value-font-size:28rpx;--sar-dropdown-option-active-color:#1580FF;--sar-dropdown-box-shadow:0rpx 6rpx 8rpx 0rpx rgba(0,0,0,0.04);"
|
||||
>
|
||||
<sar-dropdown-item v-model:visible="visibleFlag1" :title="searchParams.region || '区域'">
|
||||
<mx-radio-group
|
||||
v-model:value="searchParams.region" :options="areaList" label-key="label" value-key="value"
|
||||
custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid" @change="handleChange"
|
||||
/>
|
||||
</sar-dropdown-item>
|
||||
<sar-dropdown-item v-model:visible="visibleFlag2" :title="searchParams.natureLabel || '办学性质'">
|
||||
<mx-radio-group
|
||||
v-model:value="searchParams.nature" v-model:label="searchParams.natureLabel"
|
||||
:options="natureList" label-key="label" value-key="value" custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid" @change="handleChange"
|
||||
/>
|
||||
</sar-dropdown-item>
|
||||
<sar-dropdown-item v-model:visible="visibleFlag3" v-model="searchParams.year" :title="searchParams.year || '年份'">
|
||||
<mx-radio-group
|
||||
v-model:value="searchParams.year" :options="yearList" label-key="label" value-key="value"
|
||||
custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]" @change="handleChange"
|
||||
/>
|
||||
</sar-dropdown-item>
|
||||
</sar-dropdown>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="p-[30rpx]">
|
||||
<view
|
||||
v-for="(val, index) in partialData"
|
||||
:key="index" class="mb-[20rpx] flex justify-between gap-[40rpx] rounded-[16rpx] bg-white p-[30rpx]"
|
||||
>
|
||||
<view class="">
|
||||
<view class="max-w-[408rpx] text-wrap text-[30rpx]">
|
||||
{{ val.schoolName }}
|
||||
</view>
|
||||
<view class="mt-[10rpx] flex flex-wrap gap-[10rpx]">
|
||||
<view class="w-max px-[10rpx] py-[4rpx] text-[24rpx] text-[#666]">
|
||||
{{
|
||||
val.region }}·{{ val.schoolNature }}
|
||||
</view>
|
||||
<view
|
||||
v-for="feature in getTagList(val.tags)"
|
||||
:key="feature" class="rounded-[8rpx] bg-[#F8F8F8] px-[10rpx] py-[4rpx] text-[24rpx] text-[#666]"
|
||||
>
|
||||
{{ feature }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex items-center gap-[30rpx]">
|
||||
<view class="flex flex-col items-end text-[#1E40AF]">
|
||||
<view class="text-[40rpx] font-[DinBold]">
|
||||
{{ val.ranking ?? '--' }}
|
||||
</view>
|
||||
<view class="mt-[10rpx] w-max text-[24rpx]">
|
||||
位次
|
||||
</view>
|
||||
</view>
|
||||
<view class="flex flex-col items-end text-[#1E40AF]">
|
||||
<view class="text-[40rpx] font-[DinBold]">
|
||||
{{ val.admissionScore ?? '--' }}
|
||||
</view>
|
||||
<view class="mt-[10rpx] w-max text-[24rpx]">
|
||||
分数
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</z-paging>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
@import url('@/style/index.scss');
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ onLoad(() => {
|
|||
</view>
|
||||
</view>
|
||||
<view class="flex-1 ml-[30rpx]">
|
||||
<mx-search root-class="mr-[40rpx]" placeholder="输入学校名称" v-model:searchText="searchParams.keyword"
|
||||
<mx-search root-class="pl-[30rpx] py-[20rpx] mr-[40rpx]" placeholder="输入学校名称" v-model:searchText="searchParams.keyword"
|
||||
@complete="handleChange" />
|
||||
</view>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,191 @@
|
|||
<script lang="ts" setup>
|
||||
import { systemInfo } from '@/utils/systemInfo'
|
||||
import MxRadioGroup from "@/pages-sub/components/radio/index.vue?async"
|
||||
import MxSearch from "@/pages-sub/components/search/index.vue?async"
|
||||
import { getAreaList, getHistoryYearList, getRankTable } from "@/service"
|
||||
import { useUserStore } from "@/store"
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
definePage({
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
excludeLoginPath: true,
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
definePage({
|
||||
style: {
|
||||
navigationStyle: 'custom',
|
||||
transparentTitle: 'always',
|
||||
navigationBarTitleText: ''
|
||||
},
|
||||
excludeLoginPath: true,
|
||||
})
|
||||
// #endif
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const handleBack = () => {
|
||||
uni.navigateBack({ delta: 1 })
|
||||
}
|
||||
|
||||
const areaList = ref<any[]>([])
|
||||
|
||||
const searchParams = ref({
|
||||
score: userStore.userInfo.userExtend.expectedScore || '',
|
||||
region: userStore.userInfo.userExtend.area || null,
|
||||
nature: null,
|
||||
natureLabel: "",
|
||||
year: null
|
||||
})
|
||||
|
||||
const visibleFlag1 = ref(false)
|
||||
const visibleFlag3 = ref(false)
|
||||
|
||||
const partialData = ref<any[]>([])
|
||||
const yearList = ref<any[]>([])
|
||||
|
||||
const minScore = ref(0)
|
||||
const maxScore = ref(0)
|
||||
const exceedRatioText = ref("0%")
|
||||
const estimatedRank = ref(0)
|
||||
const nearbyItems = ref<any[]>([])
|
||||
const areaName = ref("")
|
||||
|
||||
const handleChange = () => {
|
||||
visibleFlag1.value = false;
|
||||
visibleFlag3.value = false;
|
||||
getRankTable({
|
||||
query: {
|
||||
Area: searchParams.value.region,
|
||||
Year: searchParams.value.year,
|
||||
Score: searchParams.value.score || 0
|
||||
}
|
||||
}).then(resp => {
|
||||
partialData.value = resp.result.items
|
||||
minScore.value = resp.result.minScore
|
||||
maxScore.value = resp.result.maxScore
|
||||
exceedRatioText.value = resp.result.exceedRatioText
|
||||
estimatedRank.value = resp.result.estimatedRank
|
||||
yearList.value = [...resp.result.years]
|
||||
searchParams.value.year = yearList.value[yearList.value.length - 1]?.value
|
||||
nearbyItems.value = [...resp.result.nearbyItems.reverse()]
|
||||
areaName.value=resp.result.selectedArea
|
||||
|
||||
if (searchParams.value.score === '') {
|
||||
searchParams.value.score = resp.result.minScore
|
||||
handleChange();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
|
||||
Promise.all([
|
||||
getAreaList().then(resp => {
|
||||
if (resp.code === 200) {
|
||||
areaList.value = [...resp.result]
|
||||
if(!searchParams.value.region){
|
||||
searchParams.value.region = areaList.value[0]?.value || null
|
||||
}
|
||||
}
|
||||
}),
|
||||
]).then(() => {
|
||||
handleChange()
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="gradient-custom flex flex-col h-screen">
|
||||
<sar-navbar title="中考位次" :show-back="true" @back="handleBack"
|
||||
:root-style="{ '--sar-navbar-bg': `rgba(255, 255, 255, 0)`, 'padding-top': `${systemInfo?.statusBarHeight}px`, '--sar-navbar-item-color': 'black' }">
|
||||
</sar-navbar>
|
||||
<sar-dropdown
|
||||
root-style="--sar-dropdown-placeholder-color:#666;--sar-dropdown-value-font-size:28rpx;--sar-dropdown-option-active-color:#1580FF;--sar-dropdown-box-shadow:0rpx 6rpx 8rpx 0rpx rgba(0,0,0,0.04);">
|
||||
<sar-dropdown-item v-model:visible="visibleFlag1" :title="searchParams.region || '区域'">
|
||||
<mx-radio-group v-model:value="searchParams.region" :options="areaList" label-key="label"
|
||||
value-key="value" custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid"
|
||||
@change="handleChange" />
|
||||
</sar-dropdown-item>
|
||||
<sar-dropdown-item v-model:visible="visibleFlag3" v-model="searchParams.year"
|
||||
:title="searchParams.year || '年份'">
|
||||
<mx-radio-group v-model:value="searchParams.year" :options="yearList" label-key="label"
|
||||
value-key="value" custom-root-class="px-[32rpx] pt-[30rpx] pb-[40rpx]"
|
||||
custom-item-class="w-full py-[16rpx] text-center border-[1rpx] border-solid"
|
||||
active-item-class="bg-white text-[#1580FF] border-1 border-solid border-[#1580FF]!"
|
||||
default-item-class="bg-[#F3F4F8] border-[#F3F4F8]" @change="handleChange" />
|
||||
</sar-dropdown-item>
|
||||
</sar-dropdown>
|
||||
<view class="bg-[#F8F8F8] flex-1">
|
||||
<mx-search v-model:searchText="searchParams.score" input-type="number" :searchIcon="true"
|
||||
rootStyle="margin: 32rpx 30rpx 0;background-color:#fff;" root-class="pl-[30rpx]" @submit="handleChange" />
|
||||
<view class="text-[#9E9E9E] text-[24rpx] bg-[#F8F8F8] ml-[50rpx] my-[20rpx]">支持查询180-660分</view>
|
||||
|
||||
<view class="mx-[30rpx] bg-white px-[30rpx] pt-[30rpx] pb-[20rpx] rounded-[20rpx]">
|
||||
<view class="flex items-center justify-between">
|
||||
<view class="flex flex-col">
|
||||
<view class="text-[#333] font-500 text-[28rpx]">你的预估位次</view>
|
||||
<view class="flex items-baseline text-[#333] text-[28rpx] mt-[20rpx]">
|
||||
<view>约</view>
|
||||
<view class="font-[DinBold] text-[68rpx] font-900 text-[#000] mx-[10rpx]">{{ estimatedRank }}</view>
|
||||
<view>位</view>
|
||||
</view>
|
||||
<view class="text-[24rpx] text-[#666] mt-[20rpx]">超过了{{ areaName }}{{ exceedRatioText }}的学生</view>
|
||||
</view>
|
||||
<image src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/sy_weicichaxun.png"
|
||||
mode="scaleToFill" class="w-[208rpx] h-[208rpx]" />
|
||||
</view>
|
||||
<view class="mt-[30rpx] min-h-[300rpx]">
|
||||
<view class="text-[28rpx] font-500 text-[#333] mb-[10rpx]">附近分数段</view>
|
||||
<view class="">
|
||||
<view class="grid grid-cols-4 bg-[#F3F4F8] text-[24prx] text-[#333] font-500 py-[14rpx]">
|
||||
<view class="flex items-center justify-center">分数</view>
|
||||
<view class="flex items-center justify-center">人数</view>
|
||||
<view class="flex items-center justify-center">累计人数</view>
|
||||
<view class="flex items-center justify-center">超越比例</view>
|
||||
</view>
|
||||
<view class="grid grid-cols-4 py-[8rpx]"
|
||||
:class="searchParams.score === val.score ? 'text-[#1580FF] bg-[#F3F4F8] text-[26prx] font-500' : 'text-[24prx] text-[#666] bg-[#fff]'"
|
||||
v-for="(val, index) in nearbyItems" :key="index">
|
||||
<view class="flex items-center justify-center">{{ val.score }}</view>
|
||||
<view class="flex items-center justify-center">{{ val.currentCount }}</view>
|
||||
<view class="flex items-center justify-center">{{ val.cumulativeCount }}</view>
|
||||
<view class="flex items-center justify-center">{{ val.exceedRatioText }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-[#9E9E9E] text-[24rpx] mt-[20rpx] ml-[50rpx]">注:本表数据为累计人数:即分数{{'>'}}该分数的考生总数。</view>
|
||||
<view class="mx-[40rpx] mt-[68rpx] bg-[#FEF6F6] rounded-[16rpx]">
|
||||
<view class="h-[52rpx] w-[178rpx] flex items-center m-[-8rpx] ml-[20rpx]">
|
||||
<image src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/tb_weicishuoming.png" mode="scaleToFill"
|
||||
class="h-[52rpx] w-[178rpx]" />
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col text-[#E03C33] p-[20rpx]">
|
||||
<view class="text-[24rpx] mt-[4rpx]">
|
||||
<view>位次比分数更重要!建议结合近年各高中录取位次,科学填报志愿。 </view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.gradient-custom {
|
||||
background: linear-gradient(180deg, #ecf2ff 0%, #f8f8f8 40rpx, #fff 128rpx);
|
||||
background-position: 50% 50%;
|
||||
background-origin: padding-box;
|
||||
background-clip: border-box;
|
||||
background-size: auto auto;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,14 +2,13 @@
|
|||
import { getLiveLinks, trackPromoterRedirect } from '@/service'
|
||||
|
||||
let referralCode = ''
|
||||
let weChatLiveId = ''
|
||||
let weChatLiveId = import.meta.env.VITE_WX_VIDEO_ID || ''
|
||||
let douyinLiveUrl = ''
|
||||
|
||||
onLoad((options) => {
|
||||
referralCode = options?.referralCode || ''
|
||||
|
||||
getLiveLinks().then((resp) => {
|
||||
weChatLiveId = resp?.wechatLiveUrl || ''
|
||||
douyinLiveUrl = resp?.douyinLiveUrl || ''
|
||||
}).catch((error) => {
|
||||
console.error('[live-links]', error)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,12 @@ const schools = ref(content.schools)
|
|||
<view v-for="(val, index) in schools" :key="index" class="flex mt-[26rpx]">
|
||||
<view
|
||||
class="py-[29rpx] text-[30rpx] text-[#333] grid gap-[8rpx] bg-white px-[30rpx] py-[20rpx] not-last:mb-[30rpx] rounded-[16rpx] w-full">
|
||||
<view class="text-[32rpx] font-600">{{ val.schoolName }}</view>
|
||||
<view class="text-[32rpx] font-600 flex items-center gap-[10rpx]">
|
||||
<view
|
||||
class="w-[44rpx] h-[44rpx] rounded-[8rpx] text-[26rpx] font-600 flex items-center justify-center"
|
||||
:class="`${val.tags === '稳' ? 'text-[#FA8E23] bg-[#ffeede]' : val.tags === '保' ? 'bg-[#dcf6f0] text-[#15C496]' : 'bg-[#fce5e3] text-[#EB5241]'}`">
|
||||
{{ val.tags }}</view>
|
||||
{{ val.schoolName }}</view>
|
||||
<view class="text-[24rpx] text-[#333]">{{new Date().getFullYear()}}计划招生:{{ val.planCount }}人</view>
|
||||
|
||||
<view class="text-[#333] text-[24rpx]">
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import MxSearch from "@/pages-sub/components/search/index.vue"
|
|||
import MxCheckbox from "@/pages-sub/components/checkbox/index.vue?async"
|
||||
import MxRadio from "@/pages-sub/components/radio/index.vue?async"
|
||||
import { getAreaList, getBusSchoolAdmission, getSchoolNature } from "@/service"
|
||||
import { useWishlistStore } from "@/store"
|
||||
import { useWishlistStore,useUserStore } from "@/store"
|
||||
|
||||
const tableData = ref<any[]>([])
|
||||
const tableColumns = [
|
||||
|
|
@ -26,8 +26,8 @@ const handleChoose = (val:any) => {
|
|||
chooseDataMap.delete(val.schoolId)
|
||||
}
|
||||
} else {
|
||||
if (chooseData.value.length > 2) {
|
||||
uni.showToast({ title: "志愿最多只能3个", icon: "none" })
|
||||
if (chooseData.value.length > 6) {
|
||||
uni.showToast({ title: "志愿最多只能7个", icon: "none" })
|
||||
return;
|
||||
}
|
||||
chooseData.value.push(val.schoolId);
|
||||
|
|
@ -46,6 +46,7 @@ const cwbs = ref([{ value: '冲', label: '冲' }, { value: '稳', label: '稳' }
|
|||
const natureList = ref([])
|
||||
const totalCount = ref(0)
|
||||
const regions = ref([])
|
||||
const userStore = useUserStore();
|
||||
|
||||
const searchParams = ref({
|
||||
cwb: null,
|
||||
|
|
@ -74,7 +75,8 @@ const handleChangeSchool = () => {
|
|||
schoolName: searchParams.value.keyword,
|
||||
schoolNature: searchParams.value.nature,
|
||||
region: searchParams.value.region,
|
||||
tags: searchParams.value.cwb
|
||||
tags: searchParams.value.cwb,
|
||||
score: userStore.userInfo.userExtend.expectedScore || 0
|
||||
}
|
||||
}).then(resp => {
|
||||
popVisible.value = false;
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const handleSubmit = () => {
|
|||
|
||||
let params = {
|
||||
areaName: userInfo.value.userExtend.area,
|
||||
totalScore: userInfo.value.userExtend.score,
|
||||
totalScore: userInfo.value.userExtend.expectedScore,
|
||||
id: userInfo.value.userExtend.id
|
||||
}
|
||||
saveUserAreaScore({ data: params }).then((resp) => {
|
||||
|
|
@ -128,7 +128,7 @@ onShow(() => {
|
|||
class="flex items-center text-[30rpx] justify-between mx-[40rpx] py-[34rpx] px-[30rpx] bg-[#F7F8FA] mb-[20rpx]">
|
||||
<view class="text-[#404142] text-left min-w-[150rpx] mr-[40rpx] ">总分</view>
|
||||
<view class="flex-1 flex items-center">
|
||||
<MxInput type="number" v-model:value="userInfo.userExtend.score" placeholder="请填写"
|
||||
<MxInput type="number" v-model:value="userInfo.userExtend.expectedScore" placeholder="请填写"
|
||||
root-class="text-right" class="w-full" inputDirection="text-right"/>
|
||||
|
||||
</view>
|
||||
|
|
@ -154,7 +154,7 @@ onShow(() => {
|
|||
class="h-[52rpx] w-[178rpx]" />
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col text-[#E03C33] mt-[20rpx] pl-[26rpx] pr-[34rpx]">
|
||||
<view class="flex flex-col text-[#E03C33] m-[20rpx]">
|
||||
<view class="text-[24rpx] mt-[4rpx]">
|
||||
<view>根据济南2026年中考招生政策,进入模拟志愿填报阶段的考生,默认历史、生物、地理、道法等水平考试等级均已达到普通高中基础填报要求(C级及以上),本产品仅用于统招平行志愿的模拟。</view>
|
||||
</view>
|
||||
|
|
|
|||
|
|
@ -93,11 +93,11 @@ const disableSubmit = computed(() => {
|
|||
</view>
|
||||
<view class="text-[30rpx] items-start mt-[50rpx]">
|
||||
<view class="text-[#333]">
|
||||
未被第一批次录取的考生,可填报第二批次。 可选择3所普通高中。
|
||||
</view>
|
||||
<view class="my-[20rpx] text-[#333]">
|
||||
系统按分数优先投档,平行志愿3所学校并列,不分先后。
|
||||
本工具用于模拟济南中考志愿填报。
|
||||
根据分数、区域和学校信息生成志愿表。
|
||||
结果仅供参考,最终以官方公布为准。
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view
|
||||
class="text-[34rpx] font-500 text-white bg-[#1580FF] rounded-full py-[20rpx] text-center mx-[40rpx] mt-[40rpx]"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,313 @@
|
|||
import { ref } from 'vue'
|
||||
|
||||
type ChannelLiveStatus = 1 | 2 | 3 | 4
|
||||
type ChannelNoticeStatus = 0 | 1 | 2
|
||||
type ChannelVideoType = 'live' | 'notice'
|
||||
|
||||
interface ChannelLiveInfo {
|
||||
feedId?: string
|
||||
nonceId?: string
|
||||
description?: string
|
||||
status?: ChannelLiveStatus
|
||||
headUrl?: string
|
||||
nickname?: string
|
||||
replayStatus?: string
|
||||
otherInfos?: ChannelLiveInfo[]
|
||||
}
|
||||
|
||||
interface ChannelNoticeInfo {
|
||||
noticeId?: string
|
||||
status?: ChannelNoticeStatus
|
||||
startTime?: string
|
||||
headUrl?: string
|
||||
nickname?: string
|
||||
reservable?: boolean
|
||||
otherInfos?: ChannelNoticeInfo[]
|
||||
}
|
||||
|
||||
export interface ChannelVideoItem {
|
||||
id: string
|
||||
type: ChannelVideoType
|
||||
title: string
|
||||
coverUrl: string
|
||||
statusText: string
|
||||
buttonText: string
|
||||
nickname: string
|
||||
startTimeText: string
|
||||
startTimeStamp: number
|
||||
feedId?: string
|
||||
nonceId?: string
|
||||
noticeId?: string
|
||||
reservable?: boolean
|
||||
}
|
||||
|
||||
interface ChannelsQueryApi {
|
||||
getChannelsLiveInfo: (options: {
|
||||
finderUserName: string
|
||||
success: (res: ChannelLiveInfo) => void
|
||||
fail: (err: unknown) => void
|
||||
}) => void
|
||||
getChannelsLiveNoticeInfo: (options: {
|
||||
finderUserName: string
|
||||
success: (res: ChannelNoticeInfo) => void
|
||||
fail: (err: unknown) => void
|
||||
}) => void
|
||||
}
|
||||
|
||||
interface ChannelsActionApi {
|
||||
openChannelsLive: (options: {
|
||||
finderUserName: string
|
||||
feedId?: string
|
||||
nonceId?: string
|
||||
fail: (err: unknown) => void
|
||||
}) => void
|
||||
reserveChannelsLive: (options: {
|
||||
noticeId: string
|
||||
success: () => void
|
||||
fail: (err: unknown) => void
|
||||
}) => void
|
||||
}
|
||||
|
||||
const DEFAULT_CHANNEL_COVER = 'https://lwzk.ycymedu.com/img/home/sy_daoxiao.png'
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null
|
||||
}
|
||||
|
||||
function isFunction(value: unknown): value is (...args: unknown[]) => unknown {
|
||||
return typeof value === 'function'
|
||||
}
|
||||
|
||||
function getChannelsQueryApi(): ChannelsQueryApi | null {
|
||||
const api = uni as unknown
|
||||
|
||||
if (!isRecord(api)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const canGetLiveInfo = isFunction(api.getChannelsLiveInfo)
|
||||
const canGetNoticeInfo = isFunction(api.getChannelsLiveNoticeInfo)
|
||||
|
||||
if (!canGetLiveInfo || !canGetNoticeInfo) {
|
||||
return null
|
||||
}
|
||||
|
||||
return api as unknown as ChannelsQueryApi
|
||||
}
|
||||
|
||||
function getChannelsActionApi(): ChannelsActionApi | null {
|
||||
const api = uni as unknown
|
||||
|
||||
if (!isRecord(api)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const canOpenLive = isFunction(api.openChannelsLive)
|
||||
const canReserveLive = isFunction(api.reserveChannelsLive)
|
||||
|
||||
if (!canOpenLive || !canReserveLive) {
|
||||
return null
|
||||
}
|
||||
|
||||
return api as unknown as ChannelsActionApi
|
||||
}
|
||||
|
||||
function getStartTimeStamp(startTime?: string): number {
|
||||
if (!startTime) {
|
||||
return Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
|
||||
const timestamp = Number(startTime)
|
||||
|
||||
if (Number.isFinite(timestamp)) {
|
||||
return String(Math.trunc(timestamp)).length === 10 ? timestamp * 1000 : timestamp
|
||||
}
|
||||
|
||||
const dateTime = new Date(startTime).getTime()
|
||||
|
||||
return Number.isFinite(dateTime) ? dateTime : Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
|
||||
function formatStartTime(startTime?: string): string {
|
||||
const timestamp = getStartTimeStamp(startTime)
|
||||
|
||||
if (timestamp === Number.MAX_SAFE_INTEGER) {
|
||||
return startTime ? `${startTime}开播` : '直播预告'
|
||||
}
|
||||
|
||||
const date = new Date(timestamp)
|
||||
const month = date.getMonth() + 1
|
||||
const day = date.getDate()
|
||||
const hours = String(date.getHours()).padStart(2, '0')
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
|
||||
return `${month}.${day} ${hours}:${minutes}开播`
|
||||
}
|
||||
|
||||
function flattenLiveInfo(info: ChannelLiveInfo): ChannelLiveInfo[] {
|
||||
return [info, ...(info.otherInfos ?? [])]
|
||||
}
|
||||
|
||||
function flattenNoticeInfo(info: ChannelNoticeInfo): ChannelNoticeInfo[] {
|
||||
return [info, ...(info.otherInfos ?? [])]
|
||||
}
|
||||
|
||||
function normalizeLiveInfo(info: ChannelLiveInfo, index: number): ChannelVideoItem {
|
||||
const id = info.feedId || info.nonceId || `live-${index}`
|
||||
|
||||
return {
|
||||
id: `live-${id}`,
|
||||
type: 'live',
|
||||
title: info.description || '视频号直播',
|
||||
coverUrl: info.headUrl || DEFAULT_CHANNEL_COVER,
|
||||
statusText: '正在直播',
|
||||
buttonText: '查看直播',
|
||||
nickname: info.nickname || '',
|
||||
startTimeText: '',
|
||||
startTimeStamp: 0,
|
||||
feedId: info.feedId,
|
||||
nonceId: info.nonceId,
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeNoticeInfo(info: ChannelNoticeInfo, index: number): ChannelVideoItem {
|
||||
const id = info.noticeId || `notice-${index}`
|
||||
const startTimeStamp = getStartTimeStamp(info.startTime)
|
||||
|
||||
return {
|
||||
id: `notice-${id}`,
|
||||
type: 'notice',
|
||||
title: info.nickname ? `${info.nickname}的直播预告` : '视频号直播预告',
|
||||
coverUrl: info.headUrl || DEFAULT_CHANNEL_COVER,
|
||||
statusText: formatStartTime(info.startTime),
|
||||
buttonText: info.reservable === false ? '直播预告' : '预约直播',
|
||||
nickname: info.nickname || '',
|
||||
startTimeText: formatStartTime(info.startTime),
|
||||
startTimeStamp,
|
||||
noticeId: info.noticeId,
|
||||
reservable: info.reservable,
|
||||
}
|
||||
}
|
||||
|
||||
function getLiveVideoList(finderUserName: string, channelsApi: ChannelsQueryApi): Promise<ChannelVideoItem[]> {
|
||||
return new Promise((resolve) => {
|
||||
channelsApi.getChannelsLiveInfo({
|
||||
finderUserName,
|
||||
success: (res) => {
|
||||
const liveList = flattenLiveInfo(res)
|
||||
.filter(item => item.status === 2)
|
||||
.map(normalizeLiveInfo)
|
||||
|
||||
resolve(liveList)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取视频号直播信息失败:', err)
|
||||
resolve([])
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getNoticeVideoList(finderUserName: string, channelsApi: ChannelsQueryApi): Promise<ChannelVideoItem[]> {
|
||||
return new Promise((resolve) => {
|
||||
channelsApi.getChannelsLiveNoticeInfo({
|
||||
finderUserName,
|
||||
success: (res) => {
|
||||
const noticeList = flattenNoticeInfo(res)
|
||||
.filter(item => item.status === 0)
|
||||
.map(normalizeNoticeInfo)
|
||||
.sort((prev, next) => prev.startTimeStamp - next.startTimeStamp)
|
||||
|
||||
resolve(noticeList)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('获取视频号直播预告失败:', err)
|
||||
resolve([])
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function useChannelLive(finderUserName: string) {
|
||||
const channelVideoList = ref<ChannelVideoItem[]>([])
|
||||
|
||||
function goToLiveHomePage(){
|
||||
uni.openChannelsUserProfile({
|
||||
finderUserName,
|
||||
success: () => {
|
||||
console.log('跳转视频号主页成功')
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('跳转视频号主页失败:', err)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function getChannelLiveNoticeInfo() {
|
||||
if (!finderUserName) {
|
||||
uni.showToast({ title: '直播链接获取中,请稍后再试', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const channelsApi = getChannelsQueryApi()
|
||||
|
||||
if (!channelsApi) {
|
||||
channelVideoList.value = []
|
||||
return
|
||||
}
|
||||
|
||||
const [liveList, noticeList] = await Promise.all([
|
||||
getLiveVideoList(finderUserName, channelsApi),
|
||||
getNoticeVideoList(finderUserName, channelsApi),
|
||||
])
|
||||
|
||||
channelVideoList.value = [...liveList, ...noticeList]
|
||||
}
|
||||
|
||||
function handleChannelVideoAction(item: ChannelVideoItem) {
|
||||
const channelsApi = getChannelsActionApi()
|
||||
|
||||
if (!channelsApi) {
|
||||
return
|
||||
}
|
||||
|
||||
if (item.type === 'live') {
|
||||
channelsApi.openChannelsLive({
|
||||
finderUserName,
|
||||
feedId: item.feedId,
|
||||
nonceId: item.nonceId,
|
||||
fail: (err) => {
|
||||
console.error('跳转视频号直播失败:', err)
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!item.noticeId || item.reservable === false) {
|
||||
uni.showToast({ title: '该直播暂不可预约', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
channelsApi.reserveChannelsLive({
|
||||
noticeId: item.noticeId,
|
||||
success: () => {
|
||||
uni.showModal({
|
||||
title: '预约成功',
|
||||
content: `开播时间: ${item.startTimeText} 请留意视频号消息提醒。`,
|
||||
showCancel: false,
|
||||
confirmText:'我知道了',
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('预约视频号直播失败:', err)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
channelVideoList,
|
||||
getChannelLiveNoticeInfo,
|
||||
handleChannelVideoAction,
|
||||
goToLiveHomePage
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts" setup>
|
||||
import { systemInfo } from '@/utils/systemInfo'
|
||||
import { getMyScore, getTopNew, getCountdown, getFirstPageInfo } from "@/service"
|
||||
import { getCountdown, getFirstPageInfo, getMyScore, getTopNew } from '@/service'
|
||||
import { useTokenStore } from '@/store'
|
||||
|
||||
import { systemInfo } from '@/utils/systemInfo'
|
||||
import { useChannelLive } from './hooks/useChannelLive'
|
||||
|
||||
defineOptions({
|
||||
name: 'Home',
|
||||
|
|
@ -29,6 +29,8 @@ definePage({
|
|||
// #endif
|
||||
|
||||
const tokenStore = useTokenStore()
|
||||
const wechatVideoId = import.meta.env.VITE_WX_VIDEO_ID || ''
|
||||
const { channelVideoList, getChannelLiveNoticeInfo, handleChannelVideoAction,goToLiveHomePage } = useChannelLive(wechatVideoId)
|
||||
|
||||
onShareAppMessage(() => {
|
||||
return {
|
||||
|
|
@ -43,17 +45,22 @@ onShareTimeline(() => {
|
|||
}
|
||||
})
|
||||
|
||||
const subMenus = [{ name: '中考资讯', imgPath: 'https://lwzk.ycymedu.com/img/home/sy_zixun.png', url: "/pages-sub/information/middleSchool" }, { name: '高中汇总', imgPath: 'https://lwzk.ycymedu.com/img/home/sy_huizong.png', url: '/pages-sub/information/highSchool' }, { name: '历年分数', imgPath: 'https://lwzk.ycymedu.com/img/home/sy_fenshu.png', url: '/pages-sub/information/overTheYear' }, { name: '指标生', imgPath: 'https://lwzk.ycymedu.com/img/home/sy_daoxiao.png', url: '/pages-sub/information/quota' }]
|
||||
const subMenus = [
|
||||
{ name: '历年分数', imgPath: 'https://lwzk.ycymedu.com/img/home/sy_fenshu.png', url: '/pages-sub/information/overTheYear' },
|
||||
{ name: '高中汇总', imgPath: 'https://lwzk.ycymedu.com/img/home/sy_daoxiao.png', url: '/pages-sub/information/highSchool' },
|
||||
{ name: '批次线', imgPath: 'https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/sy_picixian.png', url: '/pages-sub/information/batchline' },
|
||||
{ name: '位次查询', imgPath: 'https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/syweici.png', url: '/pages-sub/information/rank' },
|
||||
]
|
||||
|
||||
const notifies = ref([])
|
||||
const notifies = ref<any[]>([])
|
||||
|
||||
const opacity = ref(0)
|
||||
const totalScore = ref("0")
|
||||
const schoolCount = ref("0")
|
||||
const wishlistCount = ref("0")
|
||||
const totalScore = ref('0')
|
||||
const schoolCount = ref('0')
|
||||
const wishlistCount = ref('0')
|
||||
|
||||
const navigateToCreateWish = () => {
|
||||
uni.navigateTo({ url: "/pages-sub/wishlist/create/first" })
|
||||
function navigateToCreateWish() {
|
||||
uni.navigateTo({ url: '/pages-sub/wishlist/create/first' })
|
||||
}
|
||||
|
||||
onPageScroll((e) => {
|
||||
|
|
@ -61,25 +68,25 @@ onPageScroll((e) => {
|
|||
opacity.value = Math.min(scrollTop / 100, 1)
|
||||
})
|
||||
|
||||
const navigateToCountdownPage = () => {
|
||||
function navigateToCountdownPage() {
|
||||
uni.navigateTo({ url: '/pages-sub/countdown/index' })
|
||||
}
|
||||
|
||||
const navigateToSubPage = (url: string) => {
|
||||
function navigateToSubPage(url: string) {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
|
||||
const navigateToCustom = () => {
|
||||
uni.navigateTo({ url: "/pages-sub/about/onlineCustom" })
|
||||
function navigateToCustom() {
|
||||
uni.navigateTo({ url: '/pages-sub/about/onlineCustom' })
|
||||
}
|
||||
|
||||
const navigateToNewsPage = () => {
|
||||
function navigateToNewsPage() {
|
||||
uni.navigateTo({
|
||||
url: '/pages-sub/information/middleSchool'
|
||||
url: '/pages-sub/information/middleSchool',
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToNewsDetail = (id: number) => {
|
||||
function navigateToNewsDetail(id: number) {
|
||||
uni.navigateTo({ url: `/pages-sub/information/middleDetail?id=${id}` })
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +94,7 @@ const countdown = ref(0)
|
|||
|
||||
onLoad(() => {
|
||||
const updateManager = uni.getUpdateManager()
|
||||
updateManager.onCheckForUpdate(function (res) {
|
||||
updateManager.onCheckForUpdate((res) => {
|
||||
// 请求完新版本信息的回调
|
||||
if (res.hasUpdate) {
|
||||
uni.showToast({
|
||||
|
|
@ -97,11 +104,11 @@ onLoad(() => {
|
|||
}
|
||||
})
|
||||
|
||||
updateManager.onUpdateReady(function () {
|
||||
updateManager.onUpdateReady(() => {
|
||||
uni.showModal({
|
||||
title: '更新提示',
|
||||
content: '新版本已经准备好,是否重启应用?',
|
||||
success: function (res) {
|
||||
success(res) {
|
||||
if (res.confirm) {
|
||||
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
|
||||
updateManager.applyUpdate()
|
||||
|
|
@ -110,167 +117,279 @@ onLoad(() => {
|
|||
})
|
||||
})
|
||||
|
||||
updateManager.onUpdateFailed(function () {
|
||||
updateManager.onUpdateFailed(() => {
|
||||
// 新版本下载失败
|
||||
uni.showToast({
|
||||
title: '更新失败',
|
||||
icon: 'none',
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
getTopNew({ query: { Top: 3 } }).then(resp => {
|
||||
getTopNew({ query: { Top: 3 } }).then((resp) => {
|
||||
if (resp.code === 200) {
|
||||
notifies.value = resp.result
|
||||
}
|
||||
})
|
||||
|
||||
if (tokenStore.hasLogin) {
|
||||
getMyScore().then(resp => {
|
||||
getMyScore().then((resp) => {
|
||||
if (resp.code === 200 && resp.result) {
|
||||
totalScore.value = resp.result.totalScore
|
||||
}
|
||||
})
|
||||
getFirstPageInfo().then(resp => {
|
||||
getFirstPageInfo().then((resp) => {
|
||||
if (resp.code === 200) {
|
||||
schoolCount.value = resp.result.schoolCount
|
||||
wishlistCount.value = resp.result.zyCount
|
||||
}
|
||||
})
|
||||
}
|
||||
getCountdown().then(resp => {
|
||||
getCountdown().then((resp) => {
|
||||
if (resp.code === 200) {
|
||||
countdown.value = resp.result
|
||||
}
|
||||
})
|
||||
|
||||
getChannelLiveNoticeInfo()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="custom-background flex flex-col">
|
||||
<sar-navbar :fixed="true" fixation-style="top:unset;"
|
||||
:root-style="{ '--sar-navbar-bg': `rgba(255, 255, 255, ${opacity})`, '--sar-navbar-height': `${systemInfo?.statusBarHeight + 44}px` }">
|
||||
<sar-navbar
|
||||
:fixed="true" fixation-style="top:unset;"
|
||||
:root-style="{ '--sar-navbar-bg': `rgba(255, 255, 255, ${opacity})`, '--sar-navbar-height': `${systemInfo?.statusBarHeight + 44}px` }"
|
||||
>
|
||||
<template #left>
|
||||
<view class="flex items-center justify-center text-[#333] text-[32rpx] ml-[32rpx]"
|
||||
:style="{ 'padding-top': `${systemInfo?.statusBarHeight}px` }">济南</view>
|
||||
<view
|
||||
class="ml-[32rpx] flex items-center justify-center text-[32rpx] text-[#333]"
|
||||
:style="{ 'padding-top': `${systemInfo?.statusBarHeight}px` }"
|
||||
>
|
||||
济南
|
||||
</view>
|
||||
</template>
|
||||
<template #title>
|
||||
<view :style="{ 'padding-top': `${systemInfo?.statusBarHeight}px` }" class="flex justify-center">
|
||||
<view class="h-[32rpx] w-[226rpx] flex justify-center">
|
||||
<image class="h-[32rpx] w-[226rpx]" src="https://lwzk.ycymedu.com/img/home/sy_logo.png"
|
||||
mode="aspectFit" />
|
||||
<image
|
||||
class="h-[32rpx] w-[226rpx]" src="https://lwzk.ycymedu.com/img/home/sy_logo.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</sar-navbar>
|
||||
<scroll-view scroll-y class="flex-1">
|
||||
<view class="mx-[40rpx] bg-[#3D72FD] rounded-[32rpx] min-h-[460rpx] mt-[20rpx] font-[DinBold] overflow-hidden">
|
||||
<view class="mx-[40rpx] mt-[20rpx] min-h-[460rpx] overflow-hidden rounded-[32rpx] bg-[#3D72FD] font-[DinBold]">
|
||||
<view
|
||||
class="bg-[#4c7bfc] flex items-baseline justify-center rounded-[32rpx] py-[18rpx] text-white text-[36rpx] font-[JinBuFont]"
|
||||
@click="navigateToCountdownPage" v-if="countdown > 0">
|
||||
v-if="countdown > 0"
|
||||
class="flex items-baseline justify-center rounded-[32rpx] bg-[#4c7bfc] py-[18rpx] text-[36rpx] text-white font-[JinBuFont]" @click="navigateToCountdownPage"
|
||||
>
|
||||
距离中考还有 <text class="text-[52rpx] text-white">{{ countdown }}</text> 天
|
||||
<view class="w-[14rpx] h-[24rpx] ml-[10rpx]">
|
||||
<image src="https://lwzk.ycymedu.com/img/home/sy_gengduo.png" mode="scaleToFill"
|
||||
class="w-[14rpx] h-[24rpx]" />
|
||||
<view class="ml-[10rpx] h-[24rpx] w-[14rpx]">
|
||||
<image
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_gengduo.png" mode="scaleToFill"
|
||||
class="h-[24rpx] w-[14rpx]"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="bg-[#4c7bfc] flex items-baseline justify-center rounded-[32rpx] py-[18rpx] text-white text-[36rpx] font-[JinBuFont]"
|
||||
v-else>
|
||||
v-else
|
||||
class="flex items-baseline justify-center rounded-[32rpx] bg-[#4c7bfc] py-[18rpx] text-[36rpx] text-white font-[JinBuFont]"
|
||||
>
|
||||
正在考试
|
||||
</view>
|
||||
<view class="flex items-center justify-between mt-[48rpx] pl-[46rpx] pr-[64rpx]">
|
||||
<view class="flex flex-col text-white items-center gap-[8rpx]">
|
||||
<view class="mt-[48rpx] flex items-center justify-between pl-[46rpx] pr-[64rpx]">
|
||||
<view class="flex flex-col items-center gap-[8rpx] text-white">
|
||||
<view class="flex items-center">
|
||||
<text class="text-[68rpx] font-700">{{ totalScore }}</text>
|
||||
<view class="w-[30rpx] h-[30rpx] ml-[4rpx]">
|
||||
<image class="w-[30rpx] h-[30rpx]"
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_bianji.png" mode="scaleToFill" />
|
||||
<view class="ml-[4rpx] h-[30rpx] w-[30rpx]">
|
||||
<image
|
||||
class="h-[30rpx] w-[30rpx]"
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_bianji.png" mode="scaleToFill"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-[30rpx] text-[#b0c6ff]">预估成绩</view>
|
||||
<view class="text-[30rpx] text-[#b0c6ff]">
|
||||
预估成绩
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col text-white items-center gap-[8rpx]">
|
||||
<view class="flex flex-col items-center gap-[8rpx] text-white">
|
||||
<view class="flex items-baseline">
|
||||
<text class="text-[68rpx] font-700">{{ schoolCount }}</text>
|
||||
<view class="text-[32rpx] ml-[4rpx]">所</view>
|
||||
<view class="ml-[4rpx] text-[32rpx]">
|
||||
所
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-[30rpx] text-[#b0c6ff]">
|
||||
适合普高
|
||||
</view>
|
||||
<view class="text-[30rpx] text-[#b0c6ff]">适合普高</view>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col text-white items-center gap-[8rpx]">
|
||||
<view class="flex flex-col items-center gap-[8rpx] text-white">
|
||||
<view class="flex items-baseline">
|
||||
<text class="text-[68rpx] font-700">{{ wishlistCount }}</text>
|
||||
<view class="text-[32rpx] ml-[4rpx]">份</view>
|
||||
<view class="ml-[4rpx] text-[32rpx]">
|
||||
份
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-[30rpx] text-[#b0c6ff]">
|
||||
志愿表
|
||||
</view>
|
||||
<view class="text-[30rpx] text-[#b0c6ff]">志愿表</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex items-center justify-center rounded-[52rpx] py-[28rpx] mx-[74rpx] bg-gradient-to-b from-[#FCFDFF] to-[#B8D3FF] shadow-[0_8px_20px_-6px_rgba(38,129,255,0.39)] my-[48rpx]"
|
||||
@click="navigateToCreateWish">
|
||||
<view class="w-[40rpx] h-[40rpx]">
|
||||
<image class="w-[40rpx] h-[40rpx]"
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_chuangjian.png" mode="scaleToFill" />
|
||||
class="mx-[74rpx] my-[48rpx] flex items-center justify-center rounded-[52rpx] from-[#FCFDFF] to-[#B8D3FF] bg-gradient-to-b py-[28rpx] shadow-[0_8px_20px_-6px_rgba(38,129,255,0.39)]"
|
||||
@click="navigateToCreateWish"
|
||||
>
|
||||
<view class="h-[40rpx] w-[40rpx]">
|
||||
<image
|
||||
class="h-[40rpx] w-[40rpx]"
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_chuangjian.png" mode="scaleToFill"
|
||||
/>
|
||||
</view>
|
||||
<view class="ml-[8rpx] text-[40rpx] text-[#3D72FD] font-600">
|
||||
创建志愿表
|
||||
</view>
|
||||
<view class="text-[40rpx] text-[#3D72FD] ml-[8rpx] font-600">创建志愿表</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 咨询 -->
|
||||
<view class="grid grid-cols-2 mx-[32rpx] gap-[24rpx] mt-[60rpx]">
|
||||
<view class="rounded-[24rpx] bg-[#F3F4F8] py-[20rpx] pl-[30rpx] pr-[24rpx] flex items-center justify-between"
|
||||
v-for="(item, index) in subMenus" :key="index" @click="navigateToSubPage(item.url)">
|
||||
<view class="grid grid-cols-2 mx-[32rpx] mt-[60rpx] gap-[24rpx]">
|
||||
<view
|
||||
v-for="(item, index) in subMenus"
|
||||
:key="index" class="flex items-center justify-between rounded-[24rpx] bg-[#F3F4F8] py-[20rpx] pl-[30rpx] pr-[24rpx]" @click="navigateToSubPage(item.url)"
|
||||
>
|
||||
<text class="text-[34rpx] text-[#333] font-600">{{ item.name }}</text>
|
||||
<view class="w-[108rpx] h-[108rpx]">
|
||||
<image :src="item.imgPath" mode="scaleToFill" class="w-[108rpx] h-[108rpx]" />
|
||||
<view class="h-[108rpx] w-[108rpx]">
|
||||
<image :src="item.imgPath" mode="scaleToFill" class="h-[108rpx] w-[108rpx]" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mx-[30rpx] mt-[60rpx]">
|
||||
<view class="h-[216rpx]">
|
||||
<image src="https://lwzk.ycymedu.com/img/home/sy_banner.png" mode="scaleToFill"
|
||||
class="h-[216rpx]" />
|
||||
<view class="mb-[30rpx] flex items-center">
|
||||
<view class="mr-[12rpx] h-[48rpx] w-[48rpx]">
|
||||
<image
|
||||
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/sy_zhibo.png" mode="scaleToFill"
|
||||
class="h-[48rpx] w-[48rpx]"
|
||||
/>
|
||||
</view>
|
||||
<text class="text-[40rpx] text-black font-600">最新直播</text>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-x class="w-full">
|
||||
<view class="w-full flex gap-[30rpx] whitespace-nowrap" v-if="channelVideoList.length > 0">
|
||||
<view
|
||||
v-for="item in channelVideoList"
|
||||
:key="item.id"
|
||||
class="h-[430rpx] min-h-[430rpx] min-w-[440rpx] w-[440rpx] rounded-[18rpx] bg-[#F3F4F8]"
|
||||
>
|
||||
<view class="relative h-[280rpx] rounded-[18rpx]">
|
||||
<image
|
||||
:src="item.coverUrl"
|
||||
mode="scaleToFill"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
<view v-if="item.type === 'live'" class="absolute bottom-0 h-[48rpx] w-full flex items-center justify-between rounded-[0_0_18rpx_18rpx] bg-black bg-opacity-50">
|
||||
<view class="ml-[20rpx] flex items-center">
|
||||
<view class="mr-[8rpx] h-[12rpx] w-[12rpx] rounded-full bg-white" />
|
||||
<view class="text-[24rpx] text-white">
|
||||
{{ item.statusText }}
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="item.nickname" class="mr-[20rpx] flex items-center">
|
||||
<view class="mr-[10rpx] h-[20rpx] w-[30rpx] flex items-center">
|
||||
<image
|
||||
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/sy_liulan.png"
|
||||
mode="widthFix"
|
||||
/>
|
||||
</view>
|
||||
<view class="text-[24rpx] text-white">
|
||||
{{ item.nickname }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="absolute bottom-0 h-[48rpx] w-full flex items-center justify-between rounded-[0_0_18rpx_18rpx] bg-black bg-opacity-50">
|
||||
<view class="ml-[20rpx] text-[24rpx] text-white">
|
||||
{{ item.startTimeText }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="ml-[20rpx] mt-[20rpx] text-[34rpx] text-[#333] font-500">
|
||||
{{ item.title }}
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="ml-[20rpx] mt-[10rpx] h-[52rpx] w-[148rpx] flex items-center justify-center rounded-[376rpx] text-[26rpx]"
|
||||
:class="item.type === 'live' ? 'text-white bg-[#3D72FD] border-solid border-[2rpx] border-[#3D72FD]' : 'bg-[#E0E6F8] text-[#3D72FD]'"
|
||||
@click="handleChannelVideoAction(item)"
|
||||
>
|
||||
{{ item.buttonText }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="w-full flex flex-col items-center justify-center" v-if="channelVideoList.length === 0">
|
||||
<view class="w-[236rpx] h-[236rpx]">
|
||||
<image
|
||||
src="https://lw-zk.oss-cn-hangzhou.aliyuncs.com/img/home/sy_zanweikaobo.png"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
</view>
|
||||
<view @click="goToLiveHomePage()" class="mt-[20rpx] flex items-center justify-center rounded-[376rpx] text-[26rpx] text-white bg-[#3D72FD] border-solid border-[2rpx] border-[#3D72FD] px-[40rpx] py-[12rpx]"> 关注官方视频号 </view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="mx-[30rpx] mt-[60rpx]">
|
||||
<view class="flex items-center mb-[30rpx]">
|
||||
<view class="h-[48rpx] w-[48rpx] mr-[12rpx]">
|
||||
<image src="https://lwzk.ycymedu.com/img/home/sy_zixuntun.png" mode="scaleToFill"
|
||||
class="h-[48rpx] w-[48rpx]" />
|
||||
<view class="mb-[30rpx] flex items-center">
|
||||
<view class="mr-[12rpx] h-[48rpx] w-[48rpx]">
|
||||
<image
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_zixuntun.png" mode="scaleToFill"
|
||||
class="h-[48rpx] w-[48rpx]"
|
||||
/>
|
||||
</view>
|
||||
<text class="text-black text-[40rpx] font-600">最新资讯</text>
|
||||
<text class="text-[40rpx] text-black font-600">最新资讯</text>
|
||||
|
||||
<view class="text-[30rpx] text-[#A6A6A6] ml-auto" @click="navigateToNewsPage">更多</view>
|
||||
<view class="w-[14rpx] h-[30rpx] flex items-center">
|
||||
<image class="w-[14rpx] h-[30rpx]"
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_zixunjiantou.png" mode="scaleToFill" />
|
||||
<view class="ml-auto text-[30rpx] text-[#A6A6A6]" @click="navigateToNewsPage">
|
||||
更多
|
||||
</view>
|
||||
<view class="h-[30rpx] w-[14rpx] flex items-center">
|
||||
<image
|
||||
class="h-[30rpx] w-[14rpx]"
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_zixunjiantou.png" mode="scaleToFill"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="flex flex-col border-b-1 border-b-solid border-[#eee] pb-[22rpx] mb-[28rpx]"
|
||||
v-for="(value, index) in notifies" :key="index" @click="navigateToNewsDetail(value.id)">
|
||||
<text class="text-[#333] text-[32rpx] font-400">{{ value.title }}</text>
|
||||
<view class="flex items-center justify-between text-[#999] mt-[20rpx]">
|
||||
<view
|
||||
v-for="(value, index) in notifies"
|
||||
:key="index" class="mb-[28rpx] flex flex-col border-b-1 border-[#eee] border-b-solid pb-[22rpx]" @click="navigateToNewsDetail(value.id)"
|
||||
>
|
||||
<text class="text-[32rpx] text-[#333] font-400">{{ value.title }}</text>
|
||||
<view class="mt-[20rpx] flex items-center justify-between text-[#999]">
|
||||
<view class="flex items-center">
|
||||
<view class="w-[28rpx] h-[28rpx] mr-[10rpx] flex items-center">
|
||||
<image src="https://lwzk.ycymedu.com/img/home/sy_shijian.png" mode="scaleToFill"
|
||||
class="w-[28rpx] h-[28rpx]" />
|
||||
<view class="mr-[10rpx] h-[28rpx] w-[28rpx] flex items-center">
|
||||
<image
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_shijian.png" mode="scaleToFill"
|
||||
class="h-[28rpx] w-[28rpx]"
|
||||
/>
|
||||
</view>
|
||||
<text class="text-[26rpx]">{{ value.pubtime }}</text>
|
||||
</view>
|
||||
<view class="flex items-center">
|
||||
<view class="w-[28rpx] h-[28rpx] mr-[10rpx] flex items-center">
|
||||
<image src="https://lwzk.ycymedu.com/img/home/sy_chakan.png" mode="scaleToFill"
|
||||
class="w-[28rpx] h-[28rpx]" />
|
||||
<view class="mr-[10rpx] h-[28rpx] w-[28rpx] flex items-center">
|
||||
<image
|
||||
src="https://lwzk.ycymedu.com/img/home/sy_chakan.png" mode="scaleToFill"
|
||||
class="h-[28rpx] w-[28rpx]"
|
||||
/>
|
||||
</view>
|
||||
<text class="text-[26rpx]">{{ value.viewcount }}</text>
|
||||
</view>
|
||||
|
|
@ -278,9 +397,11 @@ onShow(() => {
|
|||
</view>
|
||||
</view>
|
||||
<view class="fixed bottom-[200rpx] right-[60rpx]" @click="navigateToCustom">
|
||||
<view class="w-[160rpx] h-[160rpx]">
|
||||
<image class="w-[160rpx] h-[160rpx]" src="https://lwzk.ycymedu.com/img/home/sy_kefu.png"
|
||||
mode="scaleToFill" />
|
||||
<view class="h-[160rpx] w-[160rpx]">
|
||||
<image
|
||||
class="h-[160rpx] w-[160rpx]" src="https://lwzk.ycymedu.com/img/home/sy_kefu.png"
|
||||
mode="scaleToFill"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
|
@ -289,7 +410,7 @@ onShow(() => {
|
|||
|
||||
<style scoped>
|
||||
.custom-background {
|
||||
background: linear-gradient(180deg, #C7E0FF 0%, rgba(199, 224, 255, 0) 530rpx);
|
||||
background: linear-gradient(180deg, #c7e0ff 0%, rgba(199, 224, 255, 0) 530rpx);
|
||||
background-position: 50% 50%;
|
||||
background-origin: padding-box;
|
||||
background-clip: border-box;
|
||||
|
|
|
|||
|
|
@ -157,6 +157,20 @@ export const getSchoolHistoricalScores = (options: { query: any }) => {
|
|||
});
|
||||
}
|
||||
|
||||
export const getBatchLine = (options: { query: any }) => {
|
||||
return request<API.Response>('/api/busBatchLine/h5List', {
|
||||
method: 'GET',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export const getRankTable = (options: { query: any }) => {
|
||||
return request<API.Response>('/api/busScoreSection/h5List', {
|
||||
method: 'GET',
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export const getSchoolNature = () => {
|
||||
return request<API.Response>('/api/zhiYuan/naturelist', {
|
||||
method: 'GET',
|
||||
|
|
@ -244,6 +258,12 @@ export const getMyScore = () => {
|
|||
});
|
||||
}
|
||||
|
||||
export const getShowAiStatus = () => {
|
||||
return request<API.Response>('/api/sysDictData/detail?Status=1&Id=812248362119237 ', {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
export const saveMyScore = (options: { data: any }) => {
|
||||
return request<API.Response>('/api/busMiddleSchoolApply/add', {
|
||||
method: "POST",
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
|
|
@ -61,6 +61,7 @@ export interface CustomTabBarItem {
|
|||
iconType: 'uiLib' | 'unocss' | 'iconfont' | 'image' // 不建议用 image 模式,需要配置2张图
|
||||
icon: any // 其实是 string 类型,这里是为了避免 ts 报错 (tabbar/index.vue 里面 uni-icons 那行)
|
||||
iconActive?: string // 只有在 image 模式下才需要,传递的是高亮的图片(PS: 不建议用 image 模式)
|
||||
openType?: 'switchTab' | 'navigateTo'
|
||||
badge?: CustomTabBarItemBadge
|
||||
isBulge?: boolean // 是否是中间的鼓包tabbarItem
|
||||
}
|
||||
|
|
@ -89,6 +90,18 @@ export const customTabbarList: CustomTabBarItem[] = [
|
|||
iconActive: '/static/tabbar/talented-active.png',
|
||||
// badge: 'dot',
|
||||
},
|
||||
{
|
||||
text: '',
|
||||
pagePath: 'pages-ai/ai/index',
|
||||
// 注意 unocss 图标需要如下处理:(二选一)
|
||||
// 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
||||
// 2)配置到 unocss.config.ts 的 safelist 中
|
||||
iconType: 'image',
|
||||
icon: '/static/tabbar/ai.png',
|
||||
iconActive: '/static/tabbar/ai.png',
|
||||
openType: 'navigateTo',
|
||||
// badge: 'dot',
|
||||
},
|
||||
{
|
||||
text: '测评',
|
||||
pagePath: 'pages/evaluation/index',
|
||||
|
|
@ -157,7 +170,11 @@ export const customTabbarEnable
|
|||
*/
|
||||
export const needHideNativeTabbar = selectedTabbarStrategy === TABBAR_STRATEGY_MAP.CUSTOM_TABBAR_WITH_CACHE
|
||||
|
||||
const _tabbarList = customTabbarEnable ? customTabbarList.map(item => ({ text: item.text, pagePath: item.pagePath })) : nativeTabbarList
|
||||
const _tabbarList = customTabbarEnable
|
||||
? customTabbarList
|
||||
.filter(item => item.openType !== 'navigateTo')
|
||||
.map(item => ({ text: item.text, pagePath: item.pagePath }))
|
||||
: nativeTabbarList
|
||||
export const tabbarList = customTabbarEnable ? customTabbarList : nativeTabbarList
|
||||
|
||||
const _tabbar: TabBar = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
// i-carbon-code
|
||||
import type { CustomTabBarItem } from './config'
|
||||
import { getShowAiStatus } from '@/service'
|
||||
import { customTabbarEnable, needHideNativeTabbar, tabbarCacheEnable } from './config'
|
||||
import { tabbarList, tabbarStore } from './store'
|
||||
|
||||
|
|
@ -22,16 +23,22 @@ function handleClickBulge() {
|
|||
}
|
||||
|
||||
function handleClick(index: number) {
|
||||
const item = tabbarList[index]
|
||||
|
||||
// 点击原来的不做操作
|
||||
if (index === tabbarStore.curIdx) {
|
||||
return
|
||||
}
|
||||
if (tabbarList[index].isBulge) {
|
||||
if (item.isBulge) {
|
||||
handleClickBulge()
|
||||
return
|
||||
}
|
||||
const url = tabbarList[index].pagePath
|
||||
// 点击原来的不做操作
|
||||
if (item.openType !== 'navigateTo' && index === tabbarStore.curIdx) {
|
||||
return
|
||||
}
|
||||
const url = item.pagePath
|
||||
|
||||
if (item.openType === 'navigateTo') {
|
||||
uni.navigateTo({ url })
|
||||
return
|
||||
}
|
||||
|
||||
tabbarStore.setCurIdx(index)
|
||||
if (tabbarCacheEnable) {
|
||||
|
|
@ -41,6 +48,26 @@ function handleClick(index: number) {
|
|||
uni.navigateTo({ url })
|
||||
}
|
||||
}
|
||||
|
||||
const aiShowStatus = ref("0")
|
||||
|
||||
function removeNavigateToItem() {
|
||||
const index = tabbarList.findIndex(item => item.openType === 'navigateTo')
|
||||
if (index !== -1) {
|
||||
tabbarList.splice(index, 1)
|
||||
}
|
||||
}
|
||||
|
||||
function aiShowStatusFn() {
|
||||
getShowAiStatus().then((resp) => {
|
||||
if (resp.code === 200) {
|
||||
aiShowStatus.value = resp.result.value
|
||||
if (aiShowStatus.value === "0") {
|
||||
removeNavigateToItem()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// #ifndef MP-WEIXIN
|
||||
// 因为有了 custom:true, 微信里面不需要多余的hide操作
|
||||
onLoad(() => {
|
||||
|
|
@ -69,6 +96,10 @@ function getImageByIndex(index: number, item: CustomTabBarItem) {
|
|||
}
|
||||
return tabbarStore.curIdx === index ? item.iconActive : item.icon
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
aiShowStatusFn()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -89,7 +120,7 @@ function getImageByIndex(index: number, item: CustomTabBarItem) {
|
|||
<image class="mt-6rpx h-200rpx w-200rpx" src="/static/tabbar/scan.png" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="relative px-3 flex flex-col justify-center items-center">
|
||||
<view v-else class="relative flex flex-col items-center justify-center px-3">
|
||||
<template v-if="item.iconType === 'uiLib'">
|
||||
<!-- TODO: 以下内容请根据选择的UI库自行替换 -->
|
||||
<!-- 如:<wd-icon name="home" /> (https://wot-design-uni.cn/component/icon.html) -->
|
||||
|
|
@ -101,11 +132,14 @@ function getImageByIndex(index: number, item: CustomTabBarItem) {
|
|||
<view :class="item.icon" class="text-20px" />
|
||||
</template>
|
||||
<template v-if="item.iconType === 'image'">
|
||||
<view class="h-24px w-24px">
|
||||
<view v-if="item.openType !== 'navigateTo'" class="h-24px w-24px">
|
||||
<image :src="getImageByIndex(index, item)" mode="scaleToFill" class="h-24px w-24px" />
|
||||
</view>
|
||||
<view v-if="item.openType === 'navigateTo' && aiShowStatus" class="h-48px w-42px">
|
||||
<image :src="getImageByIndex(index, item)" mode="scaleToFill" class="h-48px w-42px" />
|
||||
</view>
|
||||
</template>
|
||||
<view class="mt-2px text-14px">
|
||||
<view v-if="item.text" class="mt-2px text-14px">
|
||||
{{ item.text }}
|
||||
</view>
|
||||
<!-- 角标显示 -->
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ if (customTabbarEnable && BULGE_ENABLE) {
|
|||
|
||||
export function isPageTabbar(path: string) {
|
||||
const _path = path.split('?')[0]
|
||||
return tabbarList.some(item => item.pagePath === _path)
|
||||
return tabbarList.some(item => item.openType !== 'navigateTo' && item.pagePath === _path)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,13 +56,13 @@ const tabbarStore = reactive({
|
|||
this.setCurIdx(0)
|
||||
return
|
||||
}
|
||||
const index = tabbarList.findIndex(item => item.pagePath === path)
|
||||
const index = tabbarList.findIndex(item => item.openType !== 'navigateTo' && item.pagePath === path)
|
||||
FG_LOG_ENABLE && console.log('index:', index, path)
|
||||
// console.log('tabbarList:', tabbarList)
|
||||
if (index === -1) {
|
||||
const pagesPathList = getCurrentPages().map(item => item.route.startsWith('/') ? item.route : `/${item.route}`)
|
||||
// console.log(pagesPathList)
|
||||
const flag = tabbarList.some(item => pagesPathList.includes(item.pagePath))
|
||||
const flag = tabbarList.some(item => item.openType !== 'navigateTo' && pagesPathList.includes(item.pagePath))
|
||||
if (!flag) {
|
||||
this.setCurIdx(0)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -108,8 +108,6 @@ export function getCurrentPageI18nKey() {
|
|||
console.warn('路由不正确')
|
||||
return ''
|
||||
}
|
||||
console.log(currPage)
|
||||
console.log(currPage.style.navigationBarTitleText)
|
||||
return currPage.style?.navigationBarTitleText || ''
|
||||
}
|
||||
|
||||
|
|
@ -120,10 +118,13 @@ export function getEnvBaseUrl() {
|
|||
// 请求基准地址
|
||||
let baseUrl = import.meta.env.VITE_SERVER_BASEURL
|
||||
|
||||
// https://senior.ycymedu.com 六纬中考通
|
||||
// https://liebian.ycymedu.com 智能中专学校的
|
||||
// https://xqwgy.ycymedu.com/ 深泉外国语学院
|
||||
// # 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
|
||||
const VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://senior.ycymedu.com'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://senior.ycymedu.com'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://senior.ycymedu.com'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://xqwgy.ycymedu.com'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://xqwgy.ycymedu.com'
|
||||
const VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://xqwgy.ycymedu.com'
|
||||
|
||||
// 微信小程序端环境区分
|
||||
if (isMpWeixin) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue