refactor: clean up old Tabs component and update Card routing
- remove unused Tabs component docs, test, style and markdown files - refactor Tabs component to use inline utility classes and inline keyframes - update Card component's click handler to use named route "goods"
This commit is contained in:
@@ -1,250 +0,0 @@
|
||||
# Tab 切换组件
|
||||
|
||||
一个功能完整的 Tab 切换组件,支持动画过渡、自定义内容和响应式设计。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- ✅ **多标签切换**:支持任意数量的标签页切换
|
||||
- ✅ **动画指示器**:选中状态下划线,支持平滑滑动动画
|
||||
- ✅ **自定义内容**:支持插槽自定义标签内容
|
||||
- ✅ **响应式设计**:适配不同屏幕尺寸
|
||||
- ✅ **动态宽度**:下划线宽度根据文字宽度动态调整
|
||||
- ✅ **事件支持**:完整的切换事件和双向绑定
|
||||
- ✅ **主题定制**:支持自定义指示器颜色
|
||||
- ✅ **uniapp 兼容**:使用 uniapp 内置组件开发
|
||||
|
||||
## 基础用法
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Tab :tabs="tabList" :defaultActive="0" @change="handleTabChange" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Tab from "./components/Tab/index.vue";
|
||||
|
||||
const tabList = ref([
|
||||
{ label: "全部订单", value: "all" },
|
||||
{ label: "服务工单", value: "service" },
|
||||
]);
|
||||
|
||||
const handleTabChange = ({ index, item }) => {
|
||||
console.log("切换到:", item.label);
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## 自定义标签内容
|
||||
|
||||
使用 `tab-item` 插槽可以完全自定义标签的显示内容:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Tab :tabs="customTabs" @change="handleChange">
|
||||
<template #tab-item="{ item, index, isActive }">
|
||||
<div class="custom-tab">
|
||||
<img v-if="item.icon" :src="item.icon" class="tab-icon" />
|
||||
<text :class="{ 'active-text': isActive }">{{ item.label }}</text>
|
||||
<div v-if="item.badge" class="badge">{{ item.badge }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</Tab>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const customTabs = ref([
|
||||
{
|
||||
label: "待处理",
|
||||
value: "pending",
|
||||
icon: "/static/pending.png",
|
||||
badge: "3",
|
||||
},
|
||||
{
|
||||
label: "已完成",
|
||||
value: "completed",
|
||||
icon: "/static/completed.png",
|
||||
},
|
||||
]);
|
||||
</script>
|
||||
```
|
||||
|
||||
## 双向绑定
|
||||
|
||||
支持 `v-model` 双向绑定当前选中的索引:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Tab v-model="activeIndex" :tabs="tabList" />
|
||||
<text>当前选中索引:{{ activeIndex }}</text>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const activeIndex = ref(0);
|
||||
const tabList = ref([
|
||||
{ label: "标签1", value: "tab1" },
|
||||
{ label: "标签2", value: "tab2" },
|
||||
]);
|
||||
</script>
|
||||
```
|
||||
|
||||
## 动态标签
|
||||
|
||||
支持动态添加和删除标签:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Tab :tabs="dynamicTabs" @change="handleChange" />
|
||||
<button @click="addTab">添加标签</button>
|
||||
<button @click="removeTab">删除标签</button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const dynamicTabs = ref([{ label: "标签1", value: "tab1" }]);
|
||||
|
||||
const addTab = () => {
|
||||
const newIndex = dynamicTabs.value.length + 1;
|
||||
dynamicTabs.value.push({
|
||||
label: `标签${newIndex}`,
|
||||
value: `tab${newIndex}`,
|
||||
});
|
||||
};
|
||||
|
||||
const removeTab = () => {
|
||||
if (dynamicTabs.value.length > 1) {
|
||||
dynamicTabs.value.pop();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 参数 | 类型 | 默认值 | 说明 |
|
||||
| -------------- | ------ | --------------------------------------------------------------------- | ---------------------------- |
|
||||
| tabs | Array | `[{label:'全部订单',value:'all'},{label:'服务工单',value:'service'}]` | 标签数据数组 |
|
||||
| defaultActive | Number | `0` | 默认选中的标签索引 |
|
||||
| indicatorColor | String | `#007AFF` | 指示器颜色 |
|
||||
| modelValue | Number | - | 当前选中索引(用于 v-model) |
|
||||
|
||||
### tabs 数组项结构
|
||||
|
||||
```typescript
|
||||
interface TabItem {
|
||||
label: string; // 标签显示文本
|
||||
value: string; // 标签值
|
||||
[key: string]: any; // 其他自定义属性
|
||||
}
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
| 事件名 | 说明 | 参数 |
|
||||
| ----------------- | --------------------- | ---------------------------------- |
|
||||
| change | 标签切换时触发 | `{ index: number, item: TabItem }` |
|
||||
| update:modelValue | 用于 v-model 双向绑定 | `index: number` |
|
||||
|
||||
## Slots
|
||||
|
||||
| 插槽名 | 说明 | 作用域参数 |
|
||||
| -------- | -------------- | ----------------------------------------------------- |
|
||||
| tab-item | 自定义标签内容 | `{ item: TabItem, index: number, isActive: boolean }` |
|
||||
|
||||
## 方法
|
||||
|
||||
通过 `ref` 可以调用组件的方法:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Tab ref="tabRef" :tabs="tabList" />
|
||||
<button @click="switchToTab(1)">切换到第二个标签</button>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const tabRef = ref();
|
||||
|
||||
const switchToTab = (index) => {
|
||||
tabRef.value.setActiveIndex(index);
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
| 方法名 | 说明 | 参数 | 返回值 |
|
||||
| -------------- | ---------------------- | --------------- | --------- |
|
||||
| setActiveIndex | 设置当前选中的标签 | `index: number` | - |
|
||||
| getActiveIndex | 获取当前选中的标签索引 | - | `number` |
|
||||
| getActiveItem | 获取当前选中的标签项 | - | `TabItem` |
|
||||
|
||||
## 样式定制
|
||||
|
||||
### CSS 变量
|
||||
|
||||
组件支持通过 CSS 变量进行样式定制:
|
||||
|
||||
```scss
|
||||
.tab-container {
|
||||
--tab-bg-color: #fff; // 背景色
|
||||
--tab-text-color: #666; // 文字颜色
|
||||
--tab-active-color: #333; // 选中文字颜色
|
||||
--tab-indicator-color: #007aff; // 指示器颜色
|
||||
--tab-border-color: #f0f0f0; // 边框颜色
|
||||
}
|
||||
```
|
||||
|
||||
### 自定义主题
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<Tab :tabs="tabList" indicatorColor="#ff4d4f" class="custom-tab" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.custom-tab {
|
||||
--tab-indicator-color: #ff4d4f;
|
||||
}
|
||||
|
||||
.custom-tab .tab-text-active {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
## 技术实现
|
||||
|
||||
- **框架**:Vue 3 组合式 API
|
||||
- **平台**:uniapp 跨平台开发
|
||||
- **动画**:CSS3 transition + transform
|
||||
- **响应式**:CSS media queries
|
||||
- **兼容性**:微信小程序、H5、App
|
||||
|
||||
## 设计规范
|
||||
|
||||
- 遵循微信小程序设计规范
|
||||
- 支持无障碍访问
|
||||
- 响应式设计,适配不同设备
|
||||
- 流畅的动画过渡效果
|
||||
- 一致的视觉风格
|
||||
|
||||
## 兼容性
|
||||
|
||||
| 平台 | 支持情况 |
|
||||
| ------------ | ----------- |
|
||||
| 微信小程序 | ✅ 完全支持 |
|
||||
| H5 | ✅ 完全支持 |
|
||||
| App | ✅ 完全支持 |
|
||||
| 支付宝小程序 | ✅ 完全支持 |
|
||||
| 百度小程序 | ✅ 完全支持 |
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v1.0.0
|
||||
|
||||
- ✨ 初始版本发布
|
||||
- ✨ 支持基础标签切换功能
|
||||
- ✨ 支持动画指示器
|
||||
- ✨ 支持自定义标签内容
|
||||
- ✨ 支持响应式设计
|
||||
- ✨ 支持事件和双向绑定
|
||||
|
||||
## 备注
|
||||
|
||||
仅供学习、交流使用,请勿用于商业用途。
|
||||
@@ -1,21 +1,11 @@
|
||||
<template>
|
||||
<div class="tab-container">
|
||||
<div class="tab-wrapper">
|
||||
<div
|
||||
v-for="(item, index) in tabList"
|
||||
:key="index"
|
||||
:class="['tab-item', activeIndex === index && 'tab-item-active']"
|
||||
@click="handleTabClick(index)"
|
||||
>
|
||||
<slot
|
||||
name="tab-item"
|
||||
:item="item"
|
||||
:index="index"
|
||||
:isActive="activeIndex === index"
|
||||
>
|
||||
<span
|
||||
:class="['tab-span', activeIndex === index && 'tab-span-active']"
|
||||
>
|
||||
<div class="relative">
|
||||
<div class="flex items-center justify-center h-[30px]">
|
||||
<div v-for="(item, index) in tabList" :key="index"
|
||||
:class="['flex-1 flex items-center justify-center h-full relative transition-all duration-300 ease-out px-2', activeIndex === index && 'text-[#333] text-base font-semibold']"
|
||||
@click="handleTabClick(index)">
|
||||
<slot name="tab-item" :item="item" :index="index" :isActive="activeIndex === index">
|
||||
<span :class="['text-[#333] font-semibold', activeIndex === index && 'text-[#333] text-base font-semibold']">
|
||||
{{ item.label }}
|
||||
</span>
|
||||
</slot>
|
||||
@@ -23,17 +13,14 @@
|
||||
</div>
|
||||
|
||||
<!-- 下划线指示器 -->
|
||||
<div
|
||||
:class="[
|
||||
'tab-indicator',
|
||||
indicatorAnimating && 'animating',
|
||||
indicatorInitialized && 'initialized',
|
||||
]"
|
||||
:style="{
|
||||
left: indicatorLeft + 'px',
|
||||
width: indicatorWidth + 'px',
|
||||
}"
|
||||
></div>
|
||||
<div :class="[
|
||||
'absolute bottom-0 h-[3px] min-h-[3px] bg-[#007aff] rounded-[10px] [transition:left_0.3s_cubic-bezier(0.4,0,0.2,1),width_0.3s_cubic-bezier(0.4,0,0.2,1)] z-10 [transform:translateZ(0)] [will-change:left,width] opacity-0 w-[15px] left-0',
|
||||
indicatorAnimating && '[animation:tabSwitch_0.3s_ease-out]',
|
||||
indicatorInitialized && 'opacity-100',
|
||||
]" :style="{
|
||||
left: indicatorLeft + 'px',
|
||||
width: indicatorWidth + 'px',
|
||||
}"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -271,5 +258,15 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
@keyframes tabSwitch {
|
||||
0% {
|
||||
transform: translateZ(0) scaleX(0.8);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateZ(0) scaleX(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
.tab-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
transition: all 0.3s ease;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.tab-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
transition: all 0.3s ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tab-text-active {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tab-item-active {
|
||||
.tab-text {
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-indicator {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 3px;
|
||||
min-height: 3px; /* 确保最小高度 */
|
||||
background-color: #007aff;
|
||||
border-radius: 10px;
|
||||
transition:
|
||||
left 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
z-index: 1;
|
||||
transform: translateZ(0); /* 启用硬件加速 */
|
||||
will-change: left, width; /* 优化动画性能 */
|
||||
|
||||
/* 初始状态:未初始化时隐藏 */
|
||||
opacity: 0;
|
||||
width: 15px; /* 默认宽度15px */
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* 已初始化状态 */
|
||||
.tab-indicator.initialized {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 点击效果 */
|
||||
.tab-item:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 自定义主题色支持 */
|
||||
.tab-container[data-indicator-color="red"] .tab-indicator {
|
||||
background-color: #ff4d4f;
|
||||
}
|
||||
|
||||
.tab-container[data-indicator-color="green"] .tab-indicator {
|
||||
background-color: #52c41a;
|
||||
}
|
||||
|
||||
.tab-container[data-indicator-color="orange"] .tab-indicator {
|
||||
background-color: #fa8c16;
|
||||
}
|
||||
|
||||
/* 动画增强 */
|
||||
@keyframes tabSwitch {
|
||||
0% {
|
||||
transform: translateZ(0) scaleX(0.8);
|
||||
opacity: 0.6;
|
||||
}
|
||||
100% {
|
||||
transform: translateZ(0) scaleX(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-indicator.animating {
|
||||
animation: tabSwitch 0.3s ease-out;
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
<template>
|
||||
<div class="test-page">
|
||||
<div class="test-section">
|
||||
<span class="test-title">Tab组件测试</span>
|
||||
|
||||
<!-- 基础测试 -->
|
||||
<div class="test-item">
|
||||
<span class="item-title">基础用法</span>
|
||||
<Tab :tabs="basicTabs" :defaultActive="0" @change="handleTabChange" />
|
||||
<div class="result">
|
||||
<span>当前选中: {{ currentTab.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 多标签测试 -->
|
||||
<div class="test-item">
|
||||
<span class="item-title">多标签测试</span>
|
||||
<Tab
|
||||
:tabs="multiTabs"
|
||||
:defaultActive="1"
|
||||
@change="handleMultiTabChange"
|
||||
/>
|
||||
<div class="result">
|
||||
<span>当前选中: {{ currentMultiTab.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 快速切换测试 -->
|
||||
<div class="test-item">
|
||||
<span class="item-title">快速切换测试</span>
|
||||
<Tab
|
||||
ref="fastTabRef"
|
||||
:tabs="fastTabs"
|
||||
:defaultActive="0"
|
||||
@change="handleFastTabChange"
|
||||
/>
|
||||
<div class="result">
|
||||
<span>当前选中: {{ currentFastTab.label }}</span>
|
||||
</div>
|
||||
<div class="test-buttons">
|
||||
<button
|
||||
v-for="(tab, index) in fastTabs"
|
||||
:key="index"
|
||||
class="test-btn"
|
||||
@click="switchToTab(index)"
|
||||
>
|
||||
切换到{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 初始化测试 -->
|
||||
<div class="test-item">
|
||||
<span class="item-title">初始化测试</span>
|
||||
<span class="test-desc"
|
||||
>测试指示器的动态高度和宽度初始化及错误处理</span
|
||||
>
|
||||
<Tab
|
||||
v-if="showInitTest"
|
||||
:tabs="initTestTabs"
|
||||
:defaultActive="initActiveIndex"
|
||||
@change="handleInitTestChange"
|
||||
/>
|
||||
<div class="result">
|
||||
<span>当前选中: {{ currentInitTab.label }}</span>
|
||||
</div>
|
||||
<div class="test-buttons">
|
||||
<button class="test-btn" @click="toggleInitTest">
|
||||
{{ showInitTest ? "隐藏" : "显示" }}组件
|
||||
</button>
|
||||
<button class="test-btn" @click="changeInitActive">
|
||||
切换默认激活项 (当前: {{ initActiveIndex }})
|
||||
</button>
|
||||
<button class="test-btn" @click="addInitTab">添加Tab</button>
|
||||
<button class="test-btn" @click="removeInitTab">移除Tab</button>
|
||||
<button class="test-btn" @click="rapidToggle">快速切换测试</button>
|
||||
</div>
|
||||
<div class="test-info">
|
||||
<span>错误处理测试:组件现在能够安全处理实例为null的情况</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from "vue";
|
||||
import Tab from "./index.vue";
|
||||
|
||||
// 基础标签
|
||||
const basicTabs = ref([
|
||||
{ label: "全部订单", value: "all" },
|
||||
{ label: "服务工单", value: "service" },
|
||||
]);
|
||||
|
||||
// 多标签
|
||||
const multiTabs = ref([
|
||||
{ label: "全部", value: "all" },
|
||||
{ label: "待支付", value: "unpaid" },
|
||||
{ label: "待确认", value: "unconfirmed" },
|
||||
{ label: "进行中", value: "processing" },
|
||||
{ label: "已完成", value: "completed" },
|
||||
]);
|
||||
|
||||
// 快速切换测试
|
||||
const fastTabs = ref([
|
||||
{ label: "标签A", value: "a" },
|
||||
{ label: "标签B", value: "b" },
|
||||
{ label: "标签C", value: "c" },
|
||||
{ label: "标签D", value: "d" },
|
||||
]);
|
||||
|
||||
// 初始化测试
|
||||
const initTestTabs = ref([
|
||||
{ label: "初始1", value: "init1" },
|
||||
{ label: "初始2", value: "init2" },
|
||||
{ label: "初始3", value: "init3" },
|
||||
]);
|
||||
|
||||
// 当前选中索引
|
||||
const currentTabIndex = ref(0);
|
||||
const currentMultiTabIndex = ref(1);
|
||||
const currentFastTabIndex = ref(0);
|
||||
const currentInitTabIndex = ref(1);
|
||||
|
||||
// 初始化测试状态
|
||||
const showInitTest = ref(true);
|
||||
const initActiveIndex = ref(1);
|
||||
|
||||
// 计算当前选中项
|
||||
const currentTab = computed(() => basicTabs.value[currentTabIndex.value] || {});
|
||||
const currentMultiTab = computed(
|
||||
() => multiTabs.value[currentMultiTabIndex.value] || {},
|
||||
);
|
||||
const currentFastTab = computed(
|
||||
() => fastTabs.value[currentFastTabIndex.value] || {},
|
||||
);
|
||||
const currentInitTab = computed(
|
||||
() => initTestTabs.value[currentInitTabIndex.value] || {},
|
||||
);
|
||||
|
||||
// Tab引用
|
||||
const fastTabRef = ref(null);
|
||||
|
||||
// 事件处理
|
||||
const handleTabChange = ({ index, item }) => {
|
||||
currentTabIndex.value = index;
|
||||
console.log("基础Tab切换:", item);
|
||||
};
|
||||
|
||||
const handleMultiTabChange = ({ index, item }) => {
|
||||
currentMultiTabIndex.value = index;
|
||||
console.log("多标签Tab切换:", item);
|
||||
};
|
||||
|
||||
const handleFastTabChange = ({ index, item }) => {
|
||||
currentFastTabIndex.value = index;
|
||||
console.log("快速Tab切换:", item);
|
||||
};
|
||||
|
||||
const handleInitTestChange = ({ index, item }) => {
|
||||
currentInitTabIndex.value = index;
|
||||
console.log("初始化Tab切换:", item);
|
||||
};
|
||||
|
||||
// 程序化切换
|
||||
const switchToTab = (index) => {
|
||||
currentFastTabIndex.value = index;
|
||||
if (fastTabRef.value) {
|
||||
fastTabRef.value.setActiveIndex(index);
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化测试方法
|
||||
const toggleInitTest = () => {
|
||||
showInitTest.value = !showInitTest.value;
|
||||
};
|
||||
|
||||
const changeInitActive = () => {
|
||||
initActiveIndex.value =
|
||||
(initActiveIndex.value + 1) % initTestTabs.value.length;
|
||||
};
|
||||
|
||||
const addInitTab = () => {
|
||||
const newIndex = initTestTabs.value.length + 1;
|
||||
initTestTabs.value.push({
|
||||
label: `初始${newIndex}`,
|
||||
value: `init${newIndex}`,
|
||||
});
|
||||
};
|
||||
|
||||
const removeInitTab = () => {
|
||||
if (initTestTabs.value.length > 1) {
|
||||
initTestTabs.value.pop();
|
||||
if (initActiveIndex.value >= initTestTabs.value.length) {
|
||||
initActiveIndex.value = initTestTabs.value.length - 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const rapidToggle = () => {
|
||||
// 快速切换测试,模拟可能导致实例为null的场景
|
||||
let count = 0;
|
||||
const interval = setInterval(() => {
|
||||
showInitTest.value = !showInitTest.value;
|
||||
count++;
|
||||
if (count >= 6) {
|
||||
clearInterval(interval);
|
||||
showInitTest.value = true;
|
||||
}
|
||||
}, 200);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.test-page {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.test-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.test-item {
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.test-item:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.test-desc {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.test-info {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
background-color: #f0f9ff;
|
||||
border-radius: 8px;
|
||||
border-left: 4px solid #007aff;
|
||||
}
|
||||
|
||||
.test-info span {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.result span {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.test-buttons {
|
||||
margin-top: 15px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.test-btn {
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.test-btn:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
@@ -67,6 +67,7 @@ const navigateToPage = (commodityId, path) => {
|
||||
router.push({ path, query: { commodityId } })
|
||||
};
|
||||
|
||||
const handleClick = ({ commodityId }) =>
|
||||
navigateToPage(commodityId, "/pages/goods/index");
|
||||
const handleClick = ({ commodityId }) => {
|
||||
router.push({ name: "goods", query: { commodityId } })
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user