feat: 修改铸造页面的样式

This commit is contained in:
zheng020 2026-06-03 21:43:21 +08:00
parent 75e1222c55
commit 5f5ee0d7a1
2 changed files with 579 additions and 353 deletions

View File

@ -1,18 +1,41 @@
<template> <template>
<view class="page"> <view class="page">
<image class="bg-wrapper" src="/static/castlove/beijingban.png" mode="aspectFill"></image> <image
class="bg-wrapper"
src="/static/castlove/beijingban.png"
mode="aspectFill"
></image>
<view class="main-container"> <view class="main-container">
<view class="cards-container" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd"> <view
class="cards-container"
@touchstart="onTouchStart"
@touchmove="onTouchMove"
@touchend="onTouchEnd"
>
<view v-if="currentCardList.length"> <view v-if="currentCardList.length">
<view v-for="(card, index) in currentCardList" :key="index" class="card-item" <view
:class="{ 'no-transition': disableTransition }" :style="getCardStyle(index)"> v-for="(card, index) in currentCardList"
<view class="card-frame" :key="index"
:class="{ 'no-border': card.comingSoon, 'card-frame--tappable': !card.comingSoon }" class="card-item"
@tap.stop="onCardFrameTap(index)"> :class="{ 'no-transition': disableTransition }"
:style="getCardStyle(index)"
>
<view
class="card-frame"
:class="{
'no-border': card.comingSoon,
'card-frame--tappable': !card.comingSoon,
}"
@tap.stop="onCardFrameTap(index)"
>
<image class="card-image" :src="card.image" mode="aspectFill" /> <image class="card-image" :src="card.image" mode="aspectFill" />
<image v-if="card.comingSoon" class="coming-soon-badge" <image
src="/static/castlove/jinqingqidai.png" /> v-if="card.comingSoon"
class="coming-soon-badge"
src="/static/castlove/jinqingqidai.png"
/>
</view> </view>
<text class="card-name">{{ card.name }}</text>
</view> </view>
</view> </view>
<!-- 空态:当前分类暂无卡片时显示 --> <!-- 空态:当前分类暂无卡片时显示 -->
@ -25,12 +48,24 @@
<view v-if="showMenu" class="text-panel"> <view v-if="showMenu" class="text-panel">
<view class="arrow-btn arrow-up" @click="scrollUp"> <view class="arrow-btn arrow-up" @click="scrollUp">
<image class="arrow-icon" src="/static/castlove/jiantou.png" style="transform:rotate(180deg)" /> <image
class="arrow-icon"
src="/static/castlove/jiantou.png"
style="transform: rotate(180deg)"
/>
</view> </view>
<view class="text-list"> <view class="text-list">
<view v-for="(category, index) in categoryList" :key="index" class="text-item" <view
:class="{ active: selectedCategoryIndex === index, 'font-large': index === 1, 'font-mid': index === 0 || index === 2 }" v-for="(category, index) in categoryList"
@click="selectCategory(index)"> :key="index"
class="text-item"
:class="{
active: selectedCategoryIndex === index,
'font-large': index === 1,
'font-mid': index === 0 || index === 2,
}"
@click="selectCategory(index)"
>
<text>{{ category.name }}</text> <text>{{ category.name }}</text>
</view> </view>
</view> </view>
@ -50,7 +85,7 @@ export default {
// :star_card | badge | poster // :star_card | badge | poster
type: { type: {
type: String, type: String,
default: '', default: "",
}, },
}, },
watch: { watch: {
@ -59,29 +94,29 @@ export default {
type: { type: {
immediate: true, immediate: true,
handler(val) { handler(val) {
this.applyType(val) this.applyType(val);
}, },
}, },
}, },
created() { created() {
// type, mall.vue onLoad initialType // type, mall.vue onLoad initialType
// "" watch // "" watch
this.applyType(this.type) this.applyType(this.type);
}, },
onLoad(options) { onLoad(options) {
// ,() // ,()
this.showMenu = false this.showMenu = false;
// Tab(square.vue / CastloveContent.vue) type, // Tab(square.vue / CastloveContent.vue) type,
// type :star_card | badge | poster // type :star_card | badge | poster
if (options && options.type) { if (options && options.type) {
this.applyType(options.type) this.applyType(options.type);
} }
}, },
onShow() { onShow() {
try { try {
uni.hideToast() uni.hideToast();
uni.hideLoading() uni.hideLoading();
} catch (e) { } } catch (e) {}
}, },
data() { data() {
return { return {
@ -97,30 +132,113 @@ export default {
badge: 1, badge: 1,
poster: 2, poster: 2,
}, },
categoryList: [{ name: '星卡' }, { name: '吧唧' }, { name: '海报' }], categoryList: [{ name: "星卡" }, { name: "吧唧" }, { name: "海报" }],
cardListMap: { cardListMap: {
'星卡': [ 星卡: [
{ name: '光栅卡', image: '/static/castlove/guangshanka.png', comingSoon: false }, {
{ name: '拍立得', image: '/static/castlove/pailide.png', comingSoon: false }, name: "光栅卡",
{ name: '开发中', image: '/static/castlove/daikaifa.png', comingSoon: true }, image: "/static/castlove/guangshanka.png",
{ name: '镭射卡', image: '/static/castlove/leisheka.png', comingSoon: false }, comingSoon: false,
{ name: '撕拉片', image: '/static/castlove/silapian.png', comingSoon: false }, },
{
name: "镭射卡",
image: "/static/castlove/leisheka.png",
comingSoon: false,
},
{
name: "拍立得",
image: "/static/castlove/pailide.png",
comingSoon: false,
},
{
name: "开发中",
image: "/static/castlove/daikaifa.png",
comingSoon: true,
},
{
name: "撕拉片",
image: "/static/castlove/silapian.png",
comingSoon: false,
},
],
吧唧: [
{
name: "超复古",
image: "/static/castlove/fugu.png",
comingSoon: false,
},
{
name: "卡通刺绣",
image: "/static/castlove/katongchixiu.png",
comingSoon: false,
},
{
name: "云母片",
image: "/static/castlove/yunmupian.png",
comingSoon: false,
},
{
name: "开发中",
image: "/static/castlove/daikaifa.png",
comingSoon: true,
},
],
海报: [
{
name: "拼豆",
image: "/static/castlove/pindou.png",
comingSoon: false,
},
{
name: "简繁插画",
image: "/static/castlove/jinfanchahua.png",
comingSoon: false,
},
{
name: "街头拼贴",
image: "/static/castlove/jietoupintie.png",
comingSoon: false,
},
{
name: "开发中",
image: "/static/castlove/daikaifa.png",
comingSoon: true,
},
], ],
'吧唧': [],
'海报': []
}, },
cardList: [ cardList: [
{ name: '光栅卡', image: '/static/castlove/guangshanka.png', comingSoon: false }, {
{ name: '拍立得', image: '/static/castlove/pailide.png', comingSoon: false }, name: "光栅卡",
{ name: '镭射卡', image: '/static/castlove/leisheka.png', comingSoon: false }, image: "/static/castlove/guangshanka.png",
{ name: '撕拉片', image: '/static/castlove/silapian.png', comingSoon: false }, comingSoon: false,
{ name: '开发中', image: '/static/castlove/daikaifa.png', comingSoon: true } },
{
name: "拍立得",
image: "/static/castlove/pailide.png",
comingSoon: false,
},
{
name: "镭射卡",
image: "/static/castlove/leisheka.png",
comingSoon: false,
},
{
name: "撕拉片",
image: "/static/castlove/silapian.png",
comingSoon: false,
},
{
name: "开发中",
image: "/static/castlove/daikaifa.png",
comingSoon: true,
},
], ],
cardRoutes: { cardRoutes: {
'光栅卡': '/pages/castlove/lenticular/lenticular-create', 光栅卡: "/pages/castlove/lenticular/lenticular-create",
'拍立得': '/pages/castlove/create', 拍立得: "/pages/castlove/create",
'镭射卡': '/pages/castlove/create', 镭射卡: "/pages/castlove/create",
'撕拉片': '/pages/castlove/create', 撕拉片: "/pages/castlove/create",
}, },
totalCard: 5, totalCard: 5,
@ -130,149 +248,209 @@ export default {
isDragging: false, isDragging: false,
disableTransition: false, disableTransition: false,
SWIPE_STEP: 100, SWIPE_STEP: 100,
} };
}, },
computed: { computed: {
currentCategoryName() { currentCategoryName() {
return this.categoryList[this.selectedCategoryIndex]?.name || '' return this.categoryList[this.selectedCategoryIndex]?.name || "";
}, },
currentCardList() { currentCardList() {
const name = this.categoryList[this.selectedCategoryIndex]?.name const name = this.categoryList[this.selectedCategoryIndex]?.name;
return this.cardListMap[name] || this.cardList return this.cardListMap[name] || this.cardList;
} },
//
// ( cardListMap length ,)
currentCardCount() {
return this.currentCardList.length;
},
// ()
// N:; N:
currentCenterIndex() {
return Math.floor(this.currentCardCount / 2);
},
}, },
methods: { methods: {
// type // type
applyType(type) { applyType(type) {
if (!type) return if (!type) return;
const idx = this.categoryTypeMap[type] const idx = this.categoryTypeMap[type];
if (typeof idx === 'number') { if (typeof idx === "number") {
this.selectedCategoryIndex = idx this.selectedCategoryIndex = idx;
} }
}, },
selectCategory(index) { selectCategory(index) {
this.selectedCategoryIndex = index this.selectedCategoryIndex = index;
this.selectedIndex = 1 // ,
this.dragOffset = 0 // ( cardListMap computed tick )
this.isDragging = false const newCount =
this.cardListMap[this.categoryList[index]?.name]?.length || 0;
this.selectedIndex = Math.floor(newCount / 2);
this.dragOffset = 0;
this.isDragging = false;
}, },
onTouchStart(e) { onTouchStart(e) {
this.touchStartY = e.touches[0].clientY this.touchStartY = e.touches[0].clientY;
this.isDragging = true this.isDragging = true;
this.disableTransition = true this.disableTransition = true;
}, },
onTouchMove(e) { onTouchMove(e) {
if (!this.isDragging) return if (!this.isDragging) return;
const moveY = e.touches[0].clientY const moveY = e.touches[0].clientY;
this.dragOffset = moveY - this.touchStartY this.dragOffset = moveY - this.touchStartY;
}, },
onTouchEnd() { onTouchEnd() {
if (!this.isDragging) return if (!this.isDragging) return;
this.isDragging = false this.isDragging = false;
this.disableTransition = false this.disableTransition = false;
const moveCount = Math.round(-this.dragOffset / this.SWIPE_STEP) const N = this.currentCardCount;
let newIdx = this.selectedIndex + moveCount if (N === 0) return;
newIdx = ((newIdx % 5) + 5) % 5
this.selectedIndex = newIdx const moveCount = Math.round(-this.dragOffset / this.SWIPE_STEP);
this.dragOffset = 0 let newIdx = this.selectedIndex + moveCount;
newIdx = ((newIdx % N) + N) % N;
this.selectedIndex = newIdx;
this.dragOffset = 0;
}, },
// ====================== z-index + + ====================== // ====================== z-index + + ======================
getCardStyle(index) { //
const positions = [ // : 5
// - N <= 5: N
// - N > 5: 5 + (N-5) 沿
// /,
getStackPositions(count) {
if (count <= 0) return [];
// 5 ()
const basePositions = [
{ left: 288, top: 64, rotate: 25, scale: 0.75 }, { left: 288, top: 64, rotate: 25, scale: 0.75 },
{ left: 120, top: 288, rotate: 12, scale: 0.95 }, { left: 120, top: 288, rotate: 12, scale: 0.95 },
{ left: 60, top: 580, rotate: 0, scale: 1 }, { left: 60, top: 580, rotate: 0, scale: 1 },
{ left: 120, top: 888, rotate: -12, scale: 0.95 }, { left: 120, top: 888, rotate: -12, scale: 0.95 },
{ left: 224, top: 1096, rotate: -25, scale: 0.75 }, { left: 224, top: 1096, rotate: -25, scale: 0.75 },
] ];
if (count <= basePositions.length) {
// 5 : N ,
return basePositions.slice(0, count);
}
// 5 :,沿 5
const positions = [...basePositions];
const tail = basePositions[basePositions.length - 1];
for (let i = basePositions.length; i < count; i++) {
const extra = i - basePositions.length + 1;
positions.push({
left: tail.left,
top: tail.top + extra * 208, // 沿 8881096
rotate: tail.rotate - extra * 12, // 沿 -12°-25°
scale: tail.scale,
});
}
return positions;
},
const progress = -this.dragOffset / this.SWIPE_STEP getCardStyle(index) {
const centerIdx = this.selectedIndex + progress const N = this.currentCardCount;
if (N === 0) return {};
// const positions = this.getStackPositions(N);
let diff = index - centerIdx const centerInt = this.currentCenterIndex; // (N=52;N=42;N=63)
if (diff > 2) diff -= 5 const centerPos = positions[centerInt] ?? positions[0];
if (diff < -2) diff += 5
const cardPos = diff + 2
// const progress = -this.dragOffset / this.SWIPE_STEP;
let pos const centerIdx = this.selectedIndex + progress;
if (Number.isInteger(cardPos)) {
pos = positions[cardPos] ?? positions[2] // (): N/2 , N
let diff = index - centerIdx;
const half = N / 2;
if (diff > half) diff -= N;
if (diff < -half) diff += N;
const cardPos = diff + centerInt;
// ()
let pos;
const lowerRaw = Math.floor(cardPos);
const t = cardPos - lowerRaw;
const lowerIdx = ((lowerRaw % N) + N) % N;
const upperIdx = (lowerIdx + 1) % N;
if (t === 0 && lowerIdx >= 0 && lowerIdx < N) {
pos = positions[lowerIdx] ?? centerPos;
} else { } else {
const prev = Math.floor(cardPos) const p = positions[lowerIdx] ?? centerPos;
const next = (prev + 1) % 5 const n = positions[upperIdx] ?? centerPos;
const t = cardPos - prev
const p = positions[prev] ?? positions[2]
const n = positions[next] ?? positions[2]
pos = { pos = {
left: p.left + (n.left - p.left) * t, left: p.left + (n.left - p.left) * t,
top: p.top + (n.top - p.top) * t, top: p.top + (n.top - p.top) * t,
rotate: p.rotate + (n.rotate - p.rotate) * t, rotate: p.rotate + (n.rotate - p.rotate) * t,
scale: p.scale + (n.scale - p.scale) * t, scale: p.scale + (n.scale - p.scale) * t,
} };
} }
// ====================== ZINDEX ====================== // ====================== ZINDEX ======================
const distance = Math.abs(cardPos - 2) const distance = Math.abs(cardPos - centerInt);
const zIndex = Math.round(100 - distance * 30) // const zIndex = Math.round(100 - distance * 30); //
const isCenter = distance < 0.02 const isCenter = distance < 0.02;
if (isCenter) { if (isCenter) {
return { return {
left: pos.left + 'rpx', left: pos.left + "rpx",
top: pos.top + 'rpx', top: pos.top + "rpx",
transform: `scale(${pos.scale * 1.15}) rotate(${pos.rotate}deg)`, transform: `scale(${pos.scale * 1.15}) rotate(${pos.rotate}deg)`,
zIndex: 999, // zIndex: 999, //
} };
} }
return { return {
left: pos.left + 'rpx', left: pos.left + "rpx",
top: pos.top + 'rpx', top: pos.top + "rpx",
transform: `scale(${pos.scale}) rotate(${pos.rotate}deg)`, transform: `scale(${pos.scale}) rotate(${pos.rotate}deg)`,
zIndex, zIndex,
} };
}, },
getCardStackPosition(index) { getCardStackPosition(index) {
const progress = -this.dragOffset / this.SWIPE_STEP const N = this.currentCardCount;
const centerIdx = this.selectedIndex + progress if (N === 0) return 0;
let diff = index - centerIdx const centerInt = this.currentCenterIndex;
if (diff > 2) diff -= 5 const centerIdx = this.selectedIndex + -this.dragOffset / this.SWIPE_STEP;
if (diff < -2) diff += 5 let diff = index - centerIdx;
return diff + 2 const half = N / 2;
if (diff > half) diff -= N;
if (diff < -half) diff += N;
return diff + centerInt;
}, },
onCardFrameTap(index) { onCardFrameTap(index) {
const card = this.currentCardList[index] const card = this.currentCardList[index];
if (!card) return if (!card) return;
const pos = this.getCardStackPosition(index) const pos = this.getCardStackPosition(index);
if (Math.abs(pos - 2) < 0.2) { if (Math.abs(pos - this.currentCenterIndex) < 0.2) {
if (card.name === '光栅卡') { if (card.name === "光栅卡") {
uni.navigateTo({ uni.navigateTo({
url: this.cardRoutes['光栅卡'] + '?name=' + encodeURIComponent(card.name), url:
}) this.cardRoutes["光栅卡"] +
"?name=" +
encodeURIComponent(card.name),
});
} else { } else {
uni.showToast({ title: '激情开发中', icon: 'none' }) uni.showToast({ title: "激情开发中", icon: "none" });
} }
return return;
} }
this.selectedIndex = index this.selectedIndex = index;
}, },
scrollUp() { scrollUp() {
if (this.selectedCategoryIndex > 0) this.selectCategory(this.selectedCategoryIndex - 1) if (this.selectedCategoryIndex > 0)
this.selectCategory(this.selectedCategoryIndex - 1);
}, },
scrollDown() { scrollDown() {
if (this.selectedCategoryIndex < this.categoryList.length - 1) { if (this.selectedCategoryIndex < this.categoryList.length - 1) {
this.selectCategory(this.selectedCategoryIndex + 1) this.selectCategory(this.selectedCategoryIndex + 1);
} }
}, },
}, },
} };
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -308,8 +486,8 @@ export default {
.card-item { .card-item {
position: absolute; position: absolute;
width: 344rpx; width: 312rpx;
height: 344rpx; height: 312rpx;
transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform, z-index; will-change: transform, z-index;
} }
@ -322,9 +500,9 @@ export default {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 20rpx; border-radius: 24rpx;
padding: 10rpx; padding: 24rpx;
background-image: url('/static/square/cangpinkuang1.png'); background-image: url("/static/square/cangpinkuang1.png");
background-size: cover; background-size: cover;
} }
@ -335,7 +513,7 @@ export default {
.card-image { .card-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 14rpx; border-radius: 24rpx;
object-fit: cover; object-fit: cover;
} }
@ -348,6 +526,18 @@ export default {
height: 160rpx; height: 160rpx;
} }
.card-name {
position: absolute;
bottom: 0;
left: 32rpx;
color: #fffabd;
text-shadow: -1px 1px 4px #ce0909d6;
font-size: 32rpx;
font-weight: 400;
}
.text-panel { .text-panel {
position: absolute; position: absolute;
right: 30rpx; right: 30rpx;
@ -359,7 +549,7 @@ export default {
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: url('/static/castlove/xiahualan.png') no-repeat center; background: url("/static/castlove/xiahualan.png") no-repeat center;
background-size: 130%; background-size: 130%;
border-radius: 20rpx; border-radius: 20rpx;
} }
@ -396,7 +586,7 @@ export default {
.text-item.active { .text-item.active {
font-weight: bold; font-weight: bold;
background: url('/static/nft/dingbutubiao_liang.png') no-repeat center; background: url("/static/nft/dingbutubiao_liang.png") no-repeat center;
background-size: 100% 100%; background-size: 100% 100%;
} }

View File

@ -8,6 +8,10 @@
:show-scrollbar="false" :show-scrollbar="false"
> >
<view class="dashboard-container"> <view class="dashboard-container">
<view class="nav-back" @tap="goBack">
<text class="nav-back-icon"></text>
</view>
<DashboardHeader <DashboardHeader
:active-tab="activeTab" :active-tab="activeTab"
@update:active-tab="handleTabChange" @update:active-tab="handleTabChange"
@ -86,6 +90,18 @@ const { loading, error, data, refresh } = useDashboardData({
starId: starId.value, starId: starId.value,
}); });
//
const goBack = () => {
const pages = getCurrentPages();
if (pages.length > 1) {
uni.navigateBack();
} else {
uni.reLaunch({
url: "/pages/square/square",
});
}
};
// Tab 30 Tab cache-aware // Tab 30 Tab cache-aware
function handleTabChange(tab) { function handleTabChange(tab) {
activeTab.value = tab; activeTab.value = tab;
@ -128,6 +144,26 @@ onShow(() => {
min-height: 100vh; min-height: 100vh;
} }
.nav-back {
position: fixed;
top: 96rpx;
left: 32rpx;
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
/* background: rgba(255,255,255,0.5);
border-radius: 50%; */
z-index: 4;
}
.nav-back-icon {
font-size: 48rpx;
font-weight: bold;
color: #fff;
}
.dashboard-box { .dashboard-box {
min-height: 100vh; min-height: 100vh;
position: absolute; position: absolute;