页面细节
This commit is contained in:
parent
8ae6dcfa88
commit
ff66eeabff
|
|
@ -212,7 +212,7 @@ BEGIN
|
|||
INSERT INTO [dbo].[dict_items] ([type_id], [label], [value], [css_class], [status], [sort], [created_at])
|
||||
VALUES
|
||||
(@promotion_position_id, N'首页底部', '1', 'primary', 1, 1, GETDATE()),
|
||||
(@promotion_position_id, N'团队页面', '2', 'success', 1, 2, GETDATE());
|
||||
(@promotion_position_id, N'产品页', '2', 'success', 1, 2, GETDATE());
|
||||
PRINT N' - promotion_position items inserted';
|
||||
END
|
||||
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ public class ContentController : BusinessControllerBase
|
|||
// Position 验证
|
||||
if (request.Position != 1 && request.Position != 2)
|
||||
{
|
||||
return ValidationError("位置值无效,只能为1(首页底部)或2(团队页)");
|
||||
return ValidationError("位置值无效,只能为1(首页底部)或2(产品页)");
|
||||
}
|
||||
|
||||
try
|
||||
|
|
@ -317,7 +317,7 @@ public class ContentController : BusinessControllerBase
|
|||
// Position 验证
|
||||
if (request.Position != 1 && request.Position != 2)
|
||||
{
|
||||
return ValidationError("位置值无效,只能为1(首页底部)或2(团队页)");
|
||||
return ValidationError("位置值无效,只能为1(首页底部)或2(产品页)");
|
||||
}
|
||||
|
||||
try
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public class ContentService : IContentService
|
|||
private static readonly Dictionary<int, string> PositionNames = new()
|
||||
{
|
||||
{ 1, "首页底部" },
|
||||
{ 2, "团队页" }
|
||||
{ 2, "产品页" }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -629,7 +629,7 @@ public class ContentService : IContentService
|
|||
{
|
||||
if (position != 1 && position != 2)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.ParamError, "位置值必须为1(首页底部)或2(团队页)");
|
||||
throw new BusinessException(ErrorCodes.ParamError, "位置值必须为1(首页底部)或2(产品页)");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@
|
|||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="showLinkUrl"
|
||||
v-if="showLinkUrl && !isInternalPage"
|
||||
label="链接地址"
|
||||
prop="linkUrl"
|
||||
:required="isLinkUrlRequired"
|
||||
|
|
@ -230,6 +230,39 @@
|
|||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 内部页面选择器 -->
|
||||
<el-form-item
|
||||
v-if="isInternalPage"
|
||||
label="内部页面"
|
||||
prop="linkUrl"
|
||||
required
|
||||
>
|
||||
<el-select
|
||||
v-model="state.formData.linkUrl"
|
||||
placeholder="请选择内部页面"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option-group label="基础页面">
|
||||
<el-option
|
||||
v-for="page in staticPageOptions"
|
||||
:key="page.value"
|
||||
:label="page.label"
|
||||
:value="page.value"
|
||||
/>
|
||||
</el-option-group>
|
||||
<el-option-group v-if="productTabOptions.length > 0" label="产品页标签">
|
||||
<el-option
|
||||
v-for="tab in productTabOptions"
|
||||
:key="tab.value"
|
||||
:label="tab.label"
|
||||
:value="tab.value"
|
||||
/>
|
||||
</el-option-group>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="showAppId"
|
||||
label="小程序AppID"
|
||||
|
|
@ -287,6 +320,7 @@ import {
|
|||
deleteBanner,
|
||||
updateBannerStatus,
|
||||
updateBannerSort,
|
||||
getPromotionList,
|
||||
type BannerItem,
|
||||
type BannerQuery,
|
||||
type CreateBannerRequest,
|
||||
|
|
@ -380,7 +414,7 @@ const linkUrlPlaceholder = computed(() => {
|
|||
const linkType = Number(state.formData.linkType)
|
||||
switch (linkType) {
|
||||
case LINK_TYPE.INTERNAL:
|
||||
return '请输入内部页面路径,如:/pages/detail/index'
|
||||
return '请选择内部页面'
|
||||
case LINK_TYPE.EXTERNAL:
|
||||
return '请输入外部链接地址,如:https://example.com'
|
||||
case LINK_TYPE.MINIPROGRAM:
|
||||
|
|
@ -390,14 +424,57 @@ const linkUrlPlaceholder = computed(() => {
|
|||
}
|
||||
})
|
||||
|
||||
/** 是否为内部页面类型 */
|
||||
const isInternalPage = computed(() => {
|
||||
return Number(state.formData.linkType) === LINK_TYPE.INTERNAL
|
||||
})
|
||||
|
||||
/** 静态内部页面选项 */
|
||||
const staticPageOptions = [
|
||||
{ label: '首页', value: '/pages/index/index' },
|
||||
{ label: '产品页', value: '/pages/team/index' },
|
||||
{ label: '我的', value: '/pages/mine/index' },
|
||||
{ label: '往期测评', value: '/pages/assessment/history/index' },
|
||||
{ label: '我的订单', value: '/pages/order/list/index' },
|
||||
{ label: '学业规划', value: '/pages/planner/list/index' },
|
||||
{ label: '邀请新用户', value: '/pages/invite/index' },
|
||||
{ label: '关于', value: '/pages/about/index' }
|
||||
]
|
||||
|
||||
/** 产品页标签选项(从宣传图 position=2 动态加载) */
|
||||
const productTabOptions = ref<{ label: string; value: string }[]>([])
|
||||
|
||||
/** 加载产品页标签选项 */
|
||||
async function loadProductTabOptions() {
|
||||
try {
|
||||
const res = await getPromotionList({ page: 1, pageSize: 100, position: 2, status: 1 })
|
||||
if (res.code === 0 && res.data?.list) {
|
||||
productTabOptions.value = res.data.list.map((item, index) => ({
|
||||
label: `产品页 - ${item.title || `标签${index + 1}`}`,
|
||||
value: `/pages/team/index?tab=${index}`
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载产品页标签失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// ============ Form Rules ============
|
||||
|
||||
/** 链接地址验证器 */
|
||||
const linkUrlValidator = (_rule: any, value: string, callback: (error?: Error) => void) => {
|
||||
const linkType = Number(state.formData.linkType)
|
||||
|
||||
// 内部页面或外部链接需要 linkUrl
|
||||
if (linkType === LINK_TYPE.INTERNAL || linkType === LINK_TYPE.EXTERNAL) {
|
||||
// 内部页面需要选择页面
|
||||
if (linkType === LINK_TYPE.INTERNAL) {
|
||||
if (!value || !value.trim()) {
|
||||
callback(new Error('请选择内部页面'))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 外部链接需要 linkUrl
|
||||
if (linkType === LINK_TYPE.EXTERNAL) {
|
||||
if (!value || !value.trim()) {
|
||||
callback(new Error('请输入链接地址'))
|
||||
return
|
||||
|
|
@ -438,7 +515,7 @@ const formRules = computed<FormRules>(() => ({
|
|||
{ required: true, message: '请选择跳转类型', trigger: 'change' }
|
||||
],
|
||||
linkUrl: [
|
||||
{ validator: linkUrlValidator, trigger: 'blur' }
|
||||
{ validator: linkUrlValidator, trigger: ['blur', 'change'] }
|
||||
],
|
||||
appId: [
|
||||
{ validator: appIdValidator, trigger: 'blur' }
|
||||
|
|
@ -618,9 +695,14 @@ function handleEdit(row: BannerItem) {
|
|||
|
||||
function handleLinkTypeChange() {
|
||||
// 切换跳转类型时,清空相关字段并重新验证
|
||||
state.formData.linkUrl = ''
|
||||
if (Number(state.formData.linkType) !== LINK_TYPE.MINIPROGRAM) {
|
||||
state.formData.appId = ''
|
||||
}
|
||||
// 内部页面时加载产品页标签选项
|
||||
if (Number(state.formData.linkType) === LINK_TYPE.INTERNAL && productTabOptions.value.length === 0) {
|
||||
loadProductTabOptions()
|
||||
}
|
||||
// 触发表单重新验证
|
||||
nextTick(() => {
|
||||
formRef.value?.validateField(['linkUrl', 'appId'])
|
||||
|
|
@ -718,6 +800,7 @@ function handleDialogClosed() {
|
|||
|
||||
onMounted(() => {
|
||||
loadBannerList()
|
||||
loadProductTabOptions()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -67,3 +67,7 @@
|
|||
.disc-footer-right {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.disc-footer-right {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@ public class PdfGenerationService : IPdfGenerationService
|
|||
throw new InvalidOperationException("ReportSettings:BaseUrl 未配置");
|
||||
}
|
||||
|
||||
// 1.5 检查八大智能同分情况:>=6项同分时不生成PDF
|
||||
await CheckIntelligenceTiedScoresAsync(recordId);
|
||||
|
||||
// 2. 查询启用的页面配置,按 SortOrder 升序
|
||||
var pageConfigs = await _dbContext.ReportPageConfigs
|
||||
.Where(p => p.Status == 1)
|
||||
|
|
@ -167,6 +170,43 @@ public class PdfGenerationService : IPdfGenerationService
|
|||
recordId, reportUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查八大智能(CategoryType=1)是否存在>=6项同分的情况
|
||||
/// 如果存在,抛出异常阻止PDF生成
|
||||
/// </summary>
|
||||
/// <param name="recordId">测评记录ID</param>
|
||||
private async Task CheckIntelligenceTiedScoresAsync(long recordId)
|
||||
{
|
||||
// 查询八大智能(CategoryType=1)的测评结果
|
||||
var intelligenceScores = await _dbContext.AssessmentResults
|
||||
.AsNoTracking()
|
||||
.Join(
|
||||
_dbContext.ReportCategories.AsNoTracking().Where(c => !c.IsDeleted && c.CategoryType == 1),
|
||||
r => r.CategoryId,
|
||||
c => c.Id,
|
||||
(r, c) => new { r.RecordId, r.Percentage })
|
||||
.Where(x => x.RecordId == recordId)
|
||||
.Select(x => x.Percentage)
|
||||
.ToListAsync();
|
||||
|
||||
if (intelligenceScores.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 按百分比分组,检查是否有任意一个分数出现>=6次
|
||||
var maxTiedCount = intelligenceScores
|
||||
.GroupBy(p => p)
|
||||
.Max(g => g.Count());
|
||||
|
||||
if (maxTiedCount >= 6)
|
||||
{
|
||||
_logger.LogWarning("八大智能存在 {TiedCount} 项同分,不生成PDF,RecordId: {RecordId}",
|
||||
maxTiedCount, recordId);
|
||||
throw new InvalidOperationException($"八大智能存在{maxTiedCount}项同分(>=6项),无法生成有效的测评报告");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理单个页面配置,获取图片字节数组
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup>
|
||||
/**
|
||||
/**
|
||||
* 测评信息填写页面
|
||||
*
|
||||
* 功能:
|
||||
|
|
@ -10,34 +10,57 @@
|
|||
* - 进行中测评记录恢复(断点续答)
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { useUserStore } from '@/store/user.js'
|
||||
import { useAuth } from '@/composables/useAuth.js'
|
||||
import { usePayment } from '@/composables/usePayment.js'
|
||||
import { getIntro, verifyInviteCode, getPendingRecord } from '@/api/assessment.js'
|
||||
import { createOrder } from '@/api/order.js'
|
||||
import { isPhone } from '@/utils/validate.js'
|
||||
import Navbar from '@/components/Navbar/index.vue'
|
||||
import {
|
||||
ref,
|
||||
computed
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app'
|
||||
import {
|
||||
useUserStore
|
||||
} from '@/store/user.js'
|
||||
import {
|
||||
useAuth
|
||||
} from '@/composables/useAuth.js'
|
||||
import {
|
||||
usePayment
|
||||
} from '@/composables/usePayment.js'
|
||||
import {
|
||||
getIntro,
|
||||
verifyInviteCode,
|
||||
getPendingRecord
|
||||
} from '@/api/assessment.js'
|
||||
import {
|
||||
createOrder
|
||||
} from '@/api/order.js'
|
||||
import {
|
||||
isPhone
|
||||
} from '@/utils/validate.js'
|
||||
import Navbar from '@/components/Navbar/index.vue'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { checkLogin } = useAuth()
|
||||
const { processPayment } = usePayment()
|
||||
const userStore = useUserStore()
|
||||
const {
|
||||
checkLogin
|
||||
} = useAuth()
|
||||
const {
|
||||
processPayment
|
||||
} = usePayment()
|
||||
|
||||
// 页面参数
|
||||
const typeId = ref(0)
|
||||
const typeName = ref('')
|
||||
// 页面参数
|
||||
const typeId = ref(0)
|
||||
const typeName = ref('')
|
||||
|
||||
// 测评介绍数据
|
||||
const introData = ref({
|
||||
// 测评介绍数据
|
||||
const introData = ref({
|
||||
title: '',
|
||||
description: '',
|
||||
imageUrl: '',
|
||||
price: 0
|
||||
})
|
||||
})
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
name: '',
|
||||
phone: '',
|
||||
gender: '',
|
||||
|
|
@ -46,68 +69,79 @@ const formData = ref({
|
|||
province: '',
|
||||
city: '',
|
||||
district: ''
|
||||
})
|
||||
})
|
||||
|
||||
// 选择器数据
|
||||
const genderOptions = ['男', '女']
|
||||
const ageOptions = Array.from({ length: 41 }, (_, i) => `${i + 10}岁`)
|
||||
const educationOptions = ['小学及以下', '初中', '高中', '大专', '本科', '研究生及以上']
|
||||
// 选择器数据
|
||||
const genderOptions = ['男', '女']
|
||||
const ageOptions = Array.from({
|
||||
length: 41
|
||||
}, (_, i) => `${i + 10}岁`)
|
||||
const educationOptions = ['小学及以下', '初中', '高中', '大专', '本科', '研究生及以上']
|
||||
|
||||
/**
|
||||
/**
|
||||
* 学业阶段文本转数值
|
||||
*/
|
||||
function getEducationStageValue(label) {
|
||||
function getEducationStageValue(label) {
|
||||
const map = {
|
||||
'小学及以下': 1, '初中': 2, '高中': 3,
|
||||
'大专': 4, '本科': 4, '研究生及以上': 4
|
||||
'小学及以下': 1,
|
||||
'初中': 2,
|
||||
'高中': 3,
|
||||
'大专': 4,
|
||||
'本科': 4,
|
||||
'研究生及以上': 4
|
||||
}
|
||||
return map[label] || 1
|
||||
}
|
||||
}
|
||||
|
||||
// 选择器索引
|
||||
const genderIndex = ref(-1)
|
||||
const ageIndex = ref(-1)
|
||||
const educationIndex = ref(-1)
|
||||
// 选择器索引
|
||||
const genderIndex = ref(-1)
|
||||
const ageIndex = ref(-1)
|
||||
const educationIndex = ref(-1)
|
||||
|
||||
// 省市区数据
|
||||
const regionValue = ref([])
|
||||
// 省市区数据
|
||||
const regionValue = ref([])
|
||||
|
||||
// 邀请码弹窗
|
||||
const showInvitePopup = ref(false)
|
||||
const inviteCode = ref('')
|
||||
const inviteLoading = ref(false)
|
||||
// 邀请码弹窗
|
||||
const showInvitePopup = ref(false)
|
||||
const inviteCode = ref('')
|
||||
const inviteLoading = ref(false)
|
||||
|
||||
// 页面加载状态
|
||||
const pageLoading = ref(true)
|
||||
// 页面加载状态
|
||||
const pageLoading = ref(true)
|
||||
|
||||
// 进行中测评记录
|
||||
const pendingRecord = ref(null)
|
||||
const showPendingPopup = ref(false)
|
||||
// 进行中测评记录
|
||||
const pendingRecord = ref(null)
|
||||
const showPendingPopup = ref(false)
|
||||
|
||||
/**
|
||||
/**
|
||||
* 是否为继续测评模式
|
||||
*/
|
||||
const isContinueMode = computed(() => !!pendingRecord.value)
|
||||
const isContinueMode = computed(() => !!pendingRecord.value)
|
||||
|
||||
/**
|
||||
/**
|
||||
* 性别数值转文本
|
||||
*/
|
||||
function getGenderLabel(val) {
|
||||
function getGenderLabel(val) {
|
||||
return val === 1 ? '男' : val === 2 ? '女' : ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 学业阶段数值转文本
|
||||
*/
|
||||
function getEducationStageLabel(val) {
|
||||
const map = { 1: '小学及以下', 2: '初中', 3: '高中', 4: '大专' }
|
||||
function getEducationStageLabel(val) {
|
||||
const map = {
|
||||
1: '小学及以下',
|
||||
2: '初中',
|
||||
3: '高中',
|
||||
4: '大专'
|
||||
}
|
||||
return map[val] || ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 检查是否有进行中的测评记录
|
||||
*/
|
||||
async function checkPendingRecord() {
|
||||
async function checkPendingRecord() {
|
||||
try {
|
||||
const res = await getPendingRecord(typeId.value)
|
||||
if (res && res.code === 0 && res.data) {
|
||||
|
|
@ -117,12 +151,12 @@ async function checkPendingRecord() {
|
|||
} catch (e) {
|
||||
console.error('检查进行中测评失败:', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 继续进行中的测评
|
||||
*/
|
||||
function handleContinuePending() {
|
||||
function handleContinuePending() {
|
||||
if (!pendingRecord.value) return
|
||||
const r = pendingRecord.value
|
||||
|
||||
|
|
@ -143,20 +177,20 @@ function handleContinuePending() {
|
|||
regionValue.value = [r.province || '', r.city || '', r.district || '']
|
||||
|
||||
showPendingPopup.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 放弃进行中的测评,重新开始
|
||||
*/
|
||||
function handleDismissPending() {
|
||||
function handleDismissPending() {
|
||||
pendingRecord.value = null
|
||||
showPendingPopup.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 表单是否填写完整
|
||||
*/
|
||||
const isFormComplete = computed(() => {
|
||||
const isFormComplete = computed(() => {
|
||||
return (
|
||||
formData.value.name.trim() !== '' &&
|
||||
formData.value.phone.trim() !== '' &&
|
||||
|
|
@ -167,20 +201,20 @@ const isFormComplete = computed(() => {
|
|||
formData.value.city !== '' &&
|
||||
formData.value.district !== ''
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
/**
|
||||
* 底部按钮文案
|
||||
*/
|
||||
const payBtnText = computed(() => {
|
||||
const payBtnText = computed(() => {
|
||||
if (isContinueMode.value) return '继续测评'
|
||||
return `支付¥${introData.value.price || 20}元开始测评`
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
/**
|
||||
* 加载测评介绍
|
||||
*/
|
||||
async function loadIntro() {
|
||||
async function loadIntro() {
|
||||
pageLoading.value = true
|
||||
try {
|
||||
const res = await getIntro(typeId.value)
|
||||
|
|
@ -197,68 +231,86 @@ async function loadIntro() {
|
|||
} finally {
|
||||
pageLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 性别选择 */
|
||||
function onGenderChange(e) {
|
||||
/** 性别选择 */
|
||||
function onGenderChange(e) {
|
||||
genderIndex.value = e.detail.value
|
||||
formData.value.gender = genderOptions[e.detail.value]
|
||||
}
|
||||
}
|
||||
|
||||
/** 年龄选择 */
|
||||
function onAgeChange(e) {
|
||||
/** 年龄选择 */
|
||||
function onAgeChange(e) {
|
||||
ageIndex.value = e.detail.value
|
||||
formData.value.age = ageOptions[e.detail.value]
|
||||
}
|
||||
}
|
||||
|
||||
/** 学业阶段选择 */
|
||||
function onEducationChange(e) {
|
||||
/** 学业阶段选择 */
|
||||
function onEducationChange(e) {
|
||||
educationIndex.value = e.detail.value
|
||||
formData.value.educationStage = educationOptions[e.detail.value]
|
||||
}
|
||||
}
|
||||
|
||||
/** 省市区选择 */
|
||||
function onRegionChange(e) {
|
||||
/** 省市区选择 */
|
||||
function onRegionChange(e) {
|
||||
const value = e.detail.value
|
||||
regionValue.value = value
|
||||
formData.value.province = value[0] || ''
|
||||
formData.value.city = value[1] || ''
|
||||
formData.value.district = value[2] || ''
|
||||
}
|
||||
}
|
||||
|
||||
/** 验证表单 */
|
||||
function validateForm() {
|
||||
/** 验证表单 */
|
||||
function validateForm() {
|
||||
if (!formData.value.name.trim()) {
|
||||
uni.showToast({ title: '请输入姓名', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '请输入姓名',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (!isPhone(formData.value.phone)) {
|
||||
uni.showToast({ title: '手机号格式有误', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '手机号格式有误',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (!formData.value.gender) {
|
||||
uni.showToast({ title: '请选择性别', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '请选择性别',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (!formData.value.age) {
|
||||
uni.showToast({ title: '请选择年龄', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '请选择年龄',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (!formData.value.educationStage) {
|
||||
uni.showToast({ title: '请选择学业阶段', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '请选择学业阶段',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (!formData.value.district) {
|
||||
uni.showToast({ title: '请选择所在城市', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '请选择所在城市',
|
||||
icon: 'none'
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 构建测评信息对象
|
||||
*/
|
||||
function buildAssessmentInfo() {
|
||||
function buildAssessmentInfo() {
|
||||
return {
|
||||
name: formData.value.name,
|
||||
phone: formData.value.phone,
|
||||
|
|
@ -269,10 +321,10 @@ function buildAssessmentInfo() {
|
|||
city: formData.value.city,
|
||||
district: formData.value.district
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 支付测评 */
|
||||
async function handlePayAssessment() {
|
||||
/** 支付测评 */
|
||||
async function handlePayAssessment() {
|
||||
if (!checkLogin()) return
|
||||
|
||||
// 继续测评模式:直接跳转到答题页
|
||||
|
|
@ -286,7 +338,9 @@ async function handlePayAssessment() {
|
|||
if (!validateForm()) return
|
||||
|
||||
try {
|
||||
uni.showLoading({ title: '创建订单中...' })
|
||||
uni.showLoading({
|
||||
title: '创建订单中...'
|
||||
})
|
||||
const result = await processPayment({
|
||||
orderType: 1,
|
||||
productId: typeId.value,
|
||||
|
|
@ -301,38 +355,47 @@ async function handlePayAssessment() {
|
|||
url: `/pages/assessment/questions/index?typeId=${typeId.value}&recordId=${recordId}`
|
||||
})
|
||||
} else if (result.error) {
|
||||
uni.showToast({ title: result.error, icon: 'none' })
|
||||
uni.showToast({
|
||||
title: result.error,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
console.error('支付失败:', error)
|
||||
uni.showToast({ title: '支付失败,请重试', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '支付失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开邀请码弹窗 */
|
||||
function openInvitePopup() {
|
||||
/** 打开邀请码弹窗 */
|
||||
function openInvitePopup() {
|
||||
if (!checkLogin()) return
|
||||
if (!validateForm()) return
|
||||
inviteCode.value = ''
|
||||
showInvitePopup.value = true
|
||||
}
|
||||
}
|
||||
|
||||
/** 关闭邀请码弹窗 */
|
||||
function closeInvitePopup() {
|
||||
/** 关闭邀请码弹窗 */
|
||||
function closeInvitePopup() {
|
||||
showInvitePopup.value = false
|
||||
inviteCode.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
/** 邀请码输入处理 */
|
||||
function onInviteCodeInput(e) {
|
||||
/** 邀请码输入处理 */
|
||||
function onInviteCodeInput(e) {
|
||||
inviteCode.value = e.detail.value.toUpperCase()
|
||||
}
|
||||
}
|
||||
|
||||
/** 提交邀请码 */
|
||||
async function submitInviteCode() {
|
||||
/** 提交邀请码 */
|
||||
async function submitInviteCode() {
|
||||
if (!inviteCode.value.trim()) {
|
||||
uni.showToast({ title: '请输入邀请码', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '请输入邀请码',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -350,7 +413,9 @@ async function submitInviteCode() {
|
|||
|
||||
// 2. 邀请码有效,创建免费订单(带 inviteCodeId)
|
||||
const inviteCodeId = res.data.inviteCodeId
|
||||
uni.showLoading({ title: '创建订单中...' })
|
||||
uni.showLoading({
|
||||
title: '创建订单中...'
|
||||
})
|
||||
|
||||
const orderRes = await createOrder({
|
||||
orderType: 1,
|
||||
|
|
@ -367,19 +432,25 @@ async function submitInviteCode() {
|
|||
url: `/pages/assessment/questions/index?typeId=${typeId.value}&recordId=${recordId}`
|
||||
})
|
||||
} else {
|
||||
uni.showToast({ title: orderRes?.message || '创建订单失败', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: orderRes?.message || '创建订单失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
console.error('邀请码提交失败:', error)
|
||||
uni.showToast({ title: '提交失败,请重试', icon: 'none' })
|
||||
uni.showToast({
|
||||
title: '提交失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
inviteLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 页面加载 */
|
||||
onLoad((options) => {
|
||||
/** 页面加载 */
|
||||
onLoad((options) => {
|
||||
typeId.value = Number(options.typeId) || 1
|
||||
typeName.value = decodeURIComponent(options.typeName || '')
|
||||
userStore.restoreFromStorage()
|
||||
|
|
@ -392,7 +463,7 @@ onLoad((options) => {
|
|||
if (userStore.isLoggedIn) {
|
||||
checkPendingRecord()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -402,12 +473,7 @@ onLoad((options) => {
|
|||
|
||||
<!-- 蓝色 banner 区域 -->
|
||||
<view class="banner-section">
|
||||
<image
|
||||
v-if="introData.imageUrl"
|
||||
:src="introData.imageUrl"
|
||||
mode="widthFix"
|
||||
class="banner-image"
|
||||
/>
|
||||
<image v-if="introData.imageUrl" :src="introData.imageUrl" mode="widthFix" class="banner-image" />
|
||||
<view v-else class="banner-placeholder">
|
||||
<image src="/static/logo.png" mode="aspectFit" class="banner-logo" />
|
||||
</view>
|
||||
|
|
@ -418,19 +484,16 @@ onLoad((options) => {
|
|||
<view class="form-tip">
|
||||
{{ isContinueMode ? '您有一份进行中的测评,信息已自动填充' : '正式测评前,请先填写您的基本信息' }}
|
||||
</view>
|
||||
<view class="form-tip">
|
||||
本测评需要花费20-30分钟,且考虑到多元智能理论本身,强调智能的相对独立性和组合性,请您在决定测评题目的选项时,结合自身实际情况,尽量不要模糊,真实反映您的智能倾向。
|
||||
</view>
|
||||
|
||||
<!-- 姓名 -->
|
||||
<view class="form-item">
|
||||
<view class="form-label"><text class="required">*</text>姓名</view>
|
||||
<view class="form-input-box">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="请输入"
|
||||
placeholder-class="input-placeholder"
|
||||
v-model="formData.name"
|
||||
maxlength="20"
|
||||
:disabled="isContinueMode"
|
||||
/>
|
||||
<input type="text" placeholder="请输入" placeholder-class="input-placeholder" v-model="formData.name"
|
||||
maxlength="20" :disabled="isContinueMode" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -438,14 +501,8 @@ onLoad((options) => {
|
|||
<view class="form-item">
|
||||
<view class="form-label"><text class="required">*</text>手机号</view>
|
||||
<view class="form-input-box">
|
||||
<input
|
||||
type="number"
|
||||
placeholder="请输入"
|
||||
placeholder-class="input-placeholder"
|
||||
v-model="formData.phone"
|
||||
maxlength="11"
|
||||
:disabled="isContinueMode"
|
||||
/>
|
||||
<input type="number" placeholder="请输入" placeholder-class="input-placeholder"
|
||||
v-model="formData.phone" maxlength="11" :disabled="isContinueMode" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
|
@ -478,7 +535,8 @@ onLoad((options) => {
|
|||
<!-- 学业阶段 -->
|
||||
<view class="form-item">
|
||||
<view class="form-label"><text class="required">*</text>学业阶段</view>
|
||||
<picker mode="selector" :range="educationOptions" @change="onEducationChange" :disabled="isContinueMode">
|
||||
<picker mode="selector" :range="educationOptions" @change="onEducationChange"
|
||||
:disabled="isContinueMode">
|
||||
<view class="form-select-box">
|
||||
<text :class="formData.educationStage ? 'select-value' : 'select-placeholder'">
|
||||
{{ formData.educationStage || '请选择' }}
|
||||
|
|
@ -536,15 +594,9 @@ onLoad((options) => {
|
|||
</view>
|
||||
</view>
|
||||
<view class="popup-body">
|
||||
<input
|
||||
class="invite-input"
|
||||
type="text"
|
||||
placeholder="请输入5位邀请码"
|
||||
placeholder-style="letter-spacing: 0rpx;"
|
||||
:value="inviteCode"
|
||||
@input="onInviteCodeInput"
|
||||
maxlength="5"
|
||||
/>
|
||||
<input class="invite-input" type="text" placeholder="请输入5位邀请码"
|
||||
placeholder-style="letter-spacing: 0rpx;" :value="inviteCode" @input="onInviteCodeInput"
|
||||
maxlength="5" />
|
||||
</view>
|
||||
<view class="popup-footer">
|
||||
<view class="popup-btn" :class="{ 'btn-loading': inviteLoading }" @click="submitInviteCode">
|
||||
|
|
@ -582,16 +634,16 @@ onLoad((options) => {
|
|||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/variables.scss';
|
||||
@import '@/styles/variables.scss';
|
||||
|
||||
.assessment-info-page {
|
||||
.assessment-info-page {
|
||||
min-height: 100vh;
|
||||
background-color: rgba(255, 234, 231, 1);
|
||||
background-color: #FFFFFF;
|
||||
padding-bottom: calc(#{$spacing-xl} + env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Banner 区域 ==========
|
||||
.banner-section {
|
||||
// ========== Banner 区域 ==========
|
||||
.banner-section {
|
||||
width: 100%;
|
||||
height: 400rpx;
|
||||
background: linear-gradient(180deg, #4A90E2 0%, #6BB5FF 100%);
|
||||
|
|
@ -614,36 +666,36 @@ onLoad((options) => {
|
|||
height: 280rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 表单卡片 ==========
|
||||
.form-card {
|
||||
// ========== 表单卡片 ==========
|
||||
.form-card {
|
||||
background-color: $bg-white;
|
||||
border-radius: $border-radius-xl;
|
||||
margin: $spacing-lg $spacing-lg 0;
|
||||
padding: $spacing-xl $spacing-lg $spacing-lg;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.form-tip {
|
||||
.form-tip {
|
||||
text-align: center;
|
||||
font-size: $font-size-md;
|
||||
color: $text-secondary;
|
||||
margin-bottom: $spacing-xl;
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 表单项 ==========
|
||||
.form-item {
|
||||
// ========== 表单项 ==========
|
||||
.form-item {
|
||||
margin-bottom: $spacing-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.form-item-last {
|
||||
.form-item-last {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: $spacing-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.form-label {
|
||||
.form-label {
|
||||
font-size: $font-size-lg;
|
||||
color: $text-color;
|
||||
font-weight: $font-weight-bold;
|
||||
|
|
@ -653,10 +705,10 @@ onLoad((options) => {
|
|||
color: $error-color;
|
||||
margin-right: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 文本输入框 - 灰色背景圆角
|
||||
.form-input-box {
|
||||
// 文本输入框 - 灰色背景圆角
|
||||
.form-input-box {
|
||||
background-color: $bg-gray;
|
||||
border-radius: $border-radius-md;
|
||||
padding: 0 $spacing-lg;
|
||||
|
|
@ -670,15 +722,15 @@ onLoad((options) => {
|
|||
font-size: $font-size-md;
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-placeholder {
|
||||
.input-placeholder {
|
||||
color: $text-placeholder;
|
||||
font-size: $font-size-md;
|
||||
}
|
||||
}
|
||||
|
||||
// 下拉选择框 - 灰色背景圆角 + 下拉箭头
|
||||
.form-select-box {
|
||||
// 下拉选择框 - 灰色背景圆角 + 下拉箭头
|
||||
.form-select-box {
|
||||
background-color: $bg-gray;
|
||||
border-radius: $border-radius-md;
|
||||
padding: 0 $spacing-lg;
|
||||
|
|
@ -696,10 +748,10 @@ onLoad((options) => {
|
|||
font-size: $font-size-md;
|
||||
color: $text-placeholder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CSS 下拉箭头
|
||||
.arrow-down {
|
||||
// CSS 下拉箭头
|
||||
.arrow-down {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-right: 3rpx solid $text-placeholder;
|
||||
|
|
@ -707,24 +759,24 @@ onLoad((options) => {
|
|||
transform: rotate(45deg);
|
||||
flex-shrink: 0;
|
||||
margin-left: $spacing-sm;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow-down-sm {
|
||||
.arrow-down-sm {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-right: 3rpx solid $text-placeholder;
|
||||
border-bottom: 3rpx solid $text-placeholder;
|
||||
transform: rotate(45deg);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 省市区三列选择
|
||||
.form-region-row {
|
||||
// 省市区三列选择
|
||||
.form-region-row {
|
||||
display: flex;
|
||||
gap: $spacing-md;
|
||||
}
|
||||
}
|
||||
|
||||
.region-box {
|
||||
.region-box {
|
||||
flex: 1;
|
||||
background-color: $bg-gray;
|
||||
border-radius: $border-radius-md;
|
||||
|
|
@ -743,16 +795,16 @@ onLoad((options) => {
|
|||
font-size: $font-size-md;
|
||||
color: $text-placeholder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 底部按钮 ==========
|
||||
.btn-group {
|
||||
// ========== 底部按钮 ==========
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: $spacing-md;
|
||||
padding: $spacing-xl $spacing-lg 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-invite {
|
||||
.btn-invite {
|
||||
flex: 0.8;
|
||||
height: 88rpx;
|
||||
background-color: $bg-white;
|
||||
|
|
@ -771,9 +823,9 @@ onLoad((options) => {
|
|||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-pay {
|
||||
.btn-pay {
|
||||
flex: 1;
|
||||
height: 88rpx;
|
||||
background-color: #FF6B60;
|
||||
|
|
@ -795,10 +847,10 @@ onLoad((options) => {
|
|||
&.btn-full {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 弹窗通用 ==========
|
||||
.popup-mask {
|
||||
// ========== 弹窗通用 ==========
|
||||
.popup-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
|
@ -809,16 +861,16 @@ onLoad((options) => {
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-container {
|
||||
.popup-container {
|
||||
width: 600rpx;
|
||||
background-color: $bg-white;
|
||||
border-radius: $border-radius-xl;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-header {
|
||||
.popup-header {
|
||||
position: relative;
|
||||
padding: $spacing-lg;
|
||||
text-align: center;
|
||||
|
|
@ -846,17 +898,17 @@ onLoad((options) => {
|
|||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-body {
|
||||
.popup-body {
|
||||
padding: $spacing-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-footer {
|
||||
.popup-footer {
|
||||
padding: 0 $spacing-lg $spacing-xl;
|
||||
}
|
||||
}
|
||||
|
||||
.popup-btn {
|
||||
.popup-btn {
|
||||
height: 88rpx;
|
||||
background-color: #FF6B60;
|
||||
border-radius: 44rpx;
|
||||
|
|
@ -878,9 +930,9 @@ onLoad((options) => {
|
|||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.popup-btn-outline {
|
||||
.popup-btn-outline {
|
||||
height: 88rpx;
|
||||
background-color: $bg-white;
|
||||
border: 2rpx solid $border-color;
|
||||
|
|
@ -898,10 +950,10 @@ onLoad((options) => {
|
|||
&:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 进行中测评弹窗 ==========
|
||||
.pending-popup {
|
||||
// ========== 进行中测评弹窗 ==========
|
||||
.pending-popup {
|
||||
.pending-msg {
|
||||
font-size: $font-size-md;
|
||||
color: $text-color;
|
||||
|
|
@ -922,9 +974,9 @@ onLoad((options) => {
|
|||
color: $text-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pending-footer {
|
||||
.pending-footer {
|
||||
display: flex;
|
||||
gap: $spacing-md;
|
||||
padding: 0 $spacing-lg $spacing-xl;
|
||||
|
|
@ -933,10 +985,10 @@ onLoad((options) => {
|
|||
.popup-btn {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 邀请码输入 ==========
|
||||
.invite-input {
|
||||
// ========== 邀请码输入 ==========
|
||||
.invite-input {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
background-color: $bg-gray;
|
||||
|
|
@ -947,5 +999,5 @@ onLoad((options) => {
|
|||
text-align: center;
|
||||
letter-spacing: 16rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -362,7 +362,6 @@ onLoad((options) => {
|
|||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: rgba(255, 234, 231, 1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<!-- 有数据 -->
|
||||
<template v-if="items.length > 0">
|
||||
<!-- 自定义导航栏 -->
|
||||
<Navbar title="团队" :show-back="false" background-color="#F5A623" text-color="light" />
|
||||
<Navbar title="产品" :show-back="false" background-color="#FFFFFF" text-color="black" />
|
||||
|
||||
<!-- 顶部 tab 栏,可横向滚动 -->
|
||||
<scroll-view
|
||||
|
|
@ -72,6 +72,7 @@
|
|||
* tab 超出屏幕时可横向滚动
|
||||
*/
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { getTeamInfo } from '@/api/team.js'
|
||||
import Loading from '@/components/Loading/index.vue'
|
||||
import Navbar from '@/components/Navbar/index.vue'
|
||||
|
|
@ -82,6 +83,13 @@ const items = ref([])
|
|||
const activeIndex = ref(0)
|
||||
const scrollLeft = ref(0)
|
||||
|
||||
// 接收页面参数,支持 ?tab=0 跳转到指定标签页
|
||||
onLoad((query) => {
|
||||
if (query && query.tab !== undefined) {
|
||||
activeIndex.value = Number(query.tab) || 0
|
||||
}
|
||||
})
|
||||
|
||||
// 当前选中项
|
||||
const currentItem = computed(() => {
|
||||
return items.value[activeIndex.value] || {}
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 55 KiB |
Loading…
Reference in New Issue
Block a user