页面细节
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>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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