页面细节

This commit is contained in:
18631081161 2026-04-02 18:01:35 +08:00
parent 8ae6dcfa88
commit ff66eeabff
11 changed files with 1033 additions and 847 deletions

View File

@ -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

View File

@ -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

View File

@ -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产品页)");
}
}

View File

@ -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>

View File

@ -67,3 +67,7 @@
.disc-footer-right {
white-space: nowrap;
}
.disc-footer-right {
font-weight: 900;
}

View File

@ -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} 项同分不生成PDFRecordId: {RecordId}",
maxTiedCount, recordId);
throw new InvalidOperationException($"八大智能存在{maxTiedCount}项同分(>=6项无法生成有效的测评报告");
}
}
/// <summary>
/// 处理单个页面配置,获取图片字节数组
/// </summary>

File diff suppressed because it is too large Load Diff

View File

@ -362,7 +362,6 @@ onLoad((options) => {
height: 100vh;
display: flex;
flex-direction: column;
background-color: rgba(255, 234, 231, 1);
overflow: hidden;
}

View File

@ -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