页面细节

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>

View File

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

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