技能名称:vue3-tailwind-style
描述:提供基于 Tailwind CSS v3 的 Vue 3 项目样式配置,包括颜色方案、工具类和组件样式。用于在毕业设计项目中应用统一的界面风格。
指令:Vue 3 Tailwind 样式技能
概述
本技能提供了一套基于 Tailwind CSS v3 的 Vue 3 项目样式配置,基于属地权益业务平台运营端的界面风格。它包含了完整的颜色方案、自定义工具类和组件样式,可直接应用于毕业设计项目的前端开发。
技术栈
- Vue 3:前端框架
- Tailwind CSS v3:实用优先的 CSS 框架
- Font Awesome 4.7.0:图标库
安装和配置
1. 初始化 Vue 3 项目
npm create vite@latest my-project -- --template vue
cd my-project
npm install
2. 安装依赖
# 安装 Tailwind CSS v3
npm install -D tailwindcss postcss autoprefixer
# 初始化 Tailwind 配置
npx tailwindcss init -p
# 安装 Font Awesome
npm install @fortawesome/fontawesome-free
3. 配置 Tailwind CSS
修改 tailwind.config.js 文件:
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {
colors: {
primary: '#1890ff',
success: '#52c41a',
warning: '#faad14',
error: '#f5222d',
background: '#f0f2f5',
'card-bg': '#ffffff',
'text-primary': '#000000',
'text-secondary': '#666666',
'text-light': '#999999',
'border-color': '#e8e8e8'
},
fontFamily: {
sans: ['-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Helvetica Neue', 'Arial', 'sans-serif']
},
boxShadow: {
card: '0 2px 8px rgba(0, 0, 0, 0.15)'
}
},
},
plugins: [],
}
4. 配置 CSS
修改 src/index.css 文件:
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer utilities {
.content-auto {
content-visibility: auto;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.card {
@apply bg-card-bg rounded-md shadow-card p-4;
}
.btn {
@apply px-4 py-2 rounded-md font-medium transition-all duration-200;
}
.btn-primary {
@apply bg-primary text-white hover:bg-opacity-90;
}
.btn-default {
@apply bg-white border border-border-color hover:bg-background;
}
.btn-danger {
@apply bg-error text-white hover:bg-opacity-90;
}
.btn-success {
@apply bg-success text-white hover:bg-opacity-90;
}
.table-row-hover {
@apply hover:bg-background transition-colors duration-150;
}
.nav-item {
@apply flex items-center px-4 py-3 rounded-md transition-colors duration-150;
}
.nav-item i {
@apply w-5 text-center mr-3;
}
.nav-item-active {
@apply bg-primary bg-opacity-10 text-primary;
}
.nav-item-inactive {
@apply text-text-secondary hover:bg-background;
}
.breadcrumb {
@apply text-sm text-text-secondary;
}
.breadcrumb-active {
@apply text-text-primary;
}
.breadcrumb-separator {
@apply mx-2;
}
.badge {
@apply px-2 py-1 text-xs rounded-full;
}
.badge-success {
@apply bg-success bg-opacity-10 text-success;
}
.badge-warning {
@apply bg-warning bg-opacity-10 text-warning;
}
.badge-error {
@apply bg-error bg-opacity-10 text-error;
}
.badge-default {
@apply bg-background text-text-secondary;
}
.input {
@apply w-full px-3 py-2 border border-border-color rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:ring-opacity-50 focus:border-primary;
}
.select {
@apply w-full px-3 py-2 border border-border-color rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:ring-opacity-50 focus:border-primary appearance-none bg-white;
}
.date-picker {
@apply w-full px-3 py-2 border border-border-color rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:ring-opacity-50 focus:border-primary;
}
.tab {
@apply px-4 py-2 font-medium border-b-2 transition-colors duration-150;
}
.tab-active {
@apply text-primary border-primary;
}
.tab-inactive {
@apply text-text-secondary border-transparent hover:text-text-primary hover:border-border-color;
}
}
5. 导入样式
修改 src/main.js 文件:
import { createApp } from 'vue'
import './index.css'
import '@fortawesome/fontawesome-free/css/all.css'
import App from './App.vue'
createApp(App).mount('#app')
组件样式示例
1. 布局组件
以下内容为示例代码,仅供参考,请根据实际项目需求进行修改和调整。
主布局
<template>
<div class="bg-background font-sans text-text-primary min-h-screen flex flex-col">
<!-- 顶部导航栏 -->
<header class="bg-white border-b border-border-color h-16 flex items-center justify-between px-6 sticky top-0 z-10">
<div class="flex items-center">
<img src="@/assets/logo.png" alt="Logo" class="h-8 mr-3">
<h1 class="text-xl font-bold mr-8">项目名称</h1>
<div class="breadcrumb">
<a href="#" class="hover:text-primary">首页</a>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-active">当前页面</span>
</div>
</div>
<div class="flex items-center gap-4">
<div class="relative">
<input type="text" placeholder="搜索..." class="input w-64 pl-10">
<i class="fa fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-text-light"></i>
</div>
<div class="relative">
<button class="btn btn-default flex items-center gap-2">
<span>通知</span>
<span class="absolute -top-1 -right-1 bg-error text-white text-xs rounded-full w-5 h-5 flex items-center justify-center">3</span>
<i class="fa fa-bell-o"></i>
</button>
</div>
<div class="flex items-center gap-2">
<div class="w-8 h-8 rounded-full bg-primary text-white flex items-center justify-center">
<span>管</span>
</div>
<div>
<div class="text-sm font-medium">用户名</div>
<div class="text-xs text-text-light">管理员</div>
</div>
<button class="text-text-light hover:text-text-primary transition-colors" title="退出登录">
<i class="fa fa-sign-out"></i>
</button>
</div>
</div>
</header>
<div class="flex flex-1 overflow-hidden">
<!-- 左侧导航栏 -->
<aside class="w-64 bg-white border-r border-border-color flex flex-col" style="height: calc(100vh - 4rem);">
<nav class="flex-1 overflow-y-auto p-2">
<ul id="navMenu">
<li class="mb-1">
<a href="#" class="nav-item nav-item-active">
<i class="fa fa-tachometer"></i>
<span>数据概览</span>
</a>
</li>
<li class="mb-1">
<a href="#" class="nav-item nav-item-inactive">
<i class="fa fa-shopping-cart"></i>
<span>订单管理</span>
</a>
</li>
<li class="mb-1">
<a href="#" class="nav-item nav-item-inactive">
<i class="fa fa-building-o"></i>
<span>商户管理</span>
</a>
</li>
<li class="mb-1">
<a href="#" class="nav-item nav-item-inactive">
<i class="fa fa-th-large"></i>
<span>门店管理</span>
</a>
</li>
</ul>
</nav>
<div class="p-4 border-t border-border-color">
<div class="flex items-center gap-2">
<i class="fa fa-question-circle text-text-light"></i>
<span class="text-sm text-text-secondary">帮助中心</span>
</div>
<div class="flex items-center gap-2 mt-2">
<i class="fa fa-life-ring text-text-light"></i>
<span class="text-sm text-text-secondary">联系客服</span>
</div>
</div>
</aside>
<!-- 主内容区 -->
<main class="flex-1 overflow-y-auto p-6">
<slot></slot>
</main>
</div>
</div>
</template>
<script setup>
// 布局组件逻辑
</script>
2. 卡片组件
<template>
<div class="card mb-4">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">{{ title }}</h3>
<slot name="header-actions"></slot>
</div>
<slot></slot>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: ''
}
})
</script>
3. 表格组件
<template>
<div class="card">
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-border-color">
<th v-for="column in columns" :key="column.key" class="text-left py-3 px-4 font-medium text-text-secondary">
{{ column.title }}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in data" :key="index" class="border-b border-border-color table-row-hover">
<td v-for="column in columns" :key="column.key" class="py-3 px-4">
<slot :name="`cell-${column.key}`" :row="row" :index="index">
{{ row[column.key] }}
</slot>
</td>
</tr>
</tbody>
</table>
</div>
<div class="flex justify-between items-center mt-4">
<div class="text-sm text-text-secondary">
共 {{ data.length }} 条记录
</div>
<div class="flex items-center gap-2">
<button class="btn btn-default" :disabled="currentPage === 1">
<i class="fa fa-chevron-left"></i>
</button>
<span class="text-sm">第 {{ currentPage }} 页,共 {{ totalPages }} 页</span>
<button class="btn btn-default" :disabled="currentPage === totalPages">
<i class="fa fa-chevron-right"></i>
</button>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
columns: {
type: Array,
required: true
},
data: {
type: Array,
default: () => []
},
currentPage: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
}
})
const totalPages = computed(() => {
return Math.ceil(props.data.length / props.pageSize)
})
</script>
4. 表单组件
<template>
<div class="card">
<h3 class="text-lg font-semibold mb-4">{{ title }}</h3>
<form @submit.prevent="handleSubmit">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div v-for="field in fields" :key="field.name" class="form-item">
<label :for="field.name" class="block text-sm font-medium text-text-secondary mb-1">{{ field.label }}</label>
<input
v-if="field.type === 'text'"
:type="field.type"
:id="field.name"
:name="field.name"
v-model="formData[field.name]"
:placeholder="field.placeholder"
class="input"
/>
<select
v-else-if="field.type === 'select'"
:id="field.name"
:name="field.name"
v-model="formData[field.name]"
class="select"
>
<option v-for="option in field.options" :key="option.value" :value="option.value">
{{ option.label }}
</option>
</select>
</div>
</div>
<div class="flex justify-end gap-2">
<button type="button" class="btn btn-default" @click="handleCancel">取消</button>
<button type="submit" class="btn btn-primary">保存</button>
</div>
</form>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
const props = defineProps({
title: {
type: String,
default: '表单'
},
fields: {
type: Array,
required: true
},
initialData: {
type: Object,
default: () => {}
}
})
const emit = defineEmits(['submit', 'cancel'])
const formData = reactive({ ...props.initialData })
const handleSubmit = () => {
emit('submit', formData)
}
const handleCancel = () => {
emit('cancel')
}
</script>
页面示例
数据概览页面
<template>
<Layout>
<!-- 页面标题和操作按钮 -->
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-semibold">数据概览</h2>
<button class="btn btn-primary flex items-center gap-2">
<i class="fa fa-download"></i>
<span>导出数据</span>
</button>
</div>
<!-- 数据卡片 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div class="card">
<div class="flex items-center justify-between">
<div>
<div class="text-text-light text-sm">总订单数</div>
<div class="text-2xl font-bold mt-1">12,345</div>
<div class="text-success text-sm mt-1 flex items-center">
<i class="fa fa-arrow-up mr-1"></i> 12.5%
</div>
</div>
<div class="w-12 h-12 rounded-full bg-primary bg-opacity-10 flex items-center justify-center text-primary">
<i class="fa fa-shopping-cart text-xl"></i>
</div>
</div>
</div>
<div class="card">
<div class="flex items-center justify-between">
<div>
<div class="text-text-light text-sm">总商户数</div>
<div class="text-2xl font-bold mt-1">567</div>
<div class="text-success text-sm mt-1 flex items-center">
<i class="fa fa-arrow-up mr-1"></i> 8.3%
</div>
</div>
<div class="w-12 h-12 rounded-full bg-success bg-opacity-10 flex items-center justify-center text-success">
<i class="fa fa-building-o text-xl"></i>
</div>
</div>
</div>
<div class="card">
<div class="flex items-center justify-between">
<div>
<div class="text-text-light text-sm">总门店数</div>
<div class="text-2xl font-bold mt-1">1,234</div>
<div class="text-success text-sm mt-1 flex items-center">
<i class="fa fa-arrow-up mr-1"></i> 15.2%
</div>
</div>
<div class="w-12 h-12 rounded-full bg-warning bg-opacity-10 flex items-center justify-center text-warning">
<i class="fa fa-th-large text-xl"></i>
</div>
</div>
</div>
<div class="card">
<div class="flex items-center justify-between">
<div>
<div class="text-text-light text-sm">总销售额</div>
<div class="text-2xl font-bold mt-1">¥896,789</div>
<div class="text-success text-sm mt-1 flex items-center">
<i class="fa fa-arrow-up mr-1"></i> 23.1%
</div>
</div>
<div class="w-12 h-12 rounded-full bg-error bg-opacity-10 flex items-center justify-center text-error">
<i class="fa fa-money text-xl"></i>
</div>
</div>
</div>
</div>
<!-- 图表区域 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<div class="card">
<h3 class="text-lg font-semibold mb-4">订单趋势</h3>
<div class="h-64">
<canvas id="orderChart"></canvas>
</div>
</div>
<div class="card">
<h3 class="text-lg font-semibold mb-4">商户分布</h3>
<div class="h-64">
<canvas id="merchantChart"></canvas>
</div>
</div>
</div>
<!-- 最近订单 -->
<Card title="最近订单">
<Table
:columns="orderColumns"
:data="recentOrders"
/>
</Card>
</Layout>
</template>
<script setup>
import Layout from '@/components/Layout.vue'
import Card from '@/components/Card.vue'
import Table from '@/components/Table.vue'
import { onMounted } from 'vue'
import Chart from 'chart.js/auto'
const orderColumns = [
{ key: 'id', title: '订单号' },
{ key: 'merchant', title: '商户' },
{ key: 'amount', title: '金额' },
{ key: 'status', title: '状态' },
{ key: 'createTime', title: '创建时间' },
{ key: 'action', title: '操作' }
]
const recentOrders = [
{ id: 'ORD20260409001', merchant: '商户A', amount: '¥1,234', status: '已完成', createTime: '2026-04-09 10:30' },
{ id: 'ORD20260409002', merchant: '商户B', amount: '¥567', status: '处理中', createTime: '2026-04-09 09:15' },
{ id: 'ORD20260408001', merchant: '商户C', amount: '¥2,345', status: '已完成', createTime: '2026-04-08 16:45' },
{ id: 'ORD20260408002', merchant: '商户A', amount: '¥890', status: '已取消', createTime: '2026-04-08 14:20' },
{ id: 'ORD20260407001', merchant: '商户D', amount: '¥1,567', status: '已完成', createTime: '2026-04-07 11:00' }
]
onMounted(() => {
// 订单趋势图表
const orderCtx = document.getElementById('orderChart')
new Chart(orderCtx, {
type: 'line',
data: {
labels: ['1月', '2月', '3月', '4月', '5月', '6月'],
datasets: [{
label: '订单数',
data: [1200, 1900, 3000, 5000, 8000, 12000],
borderColor: '#1890ff',
backgroundColor: 'rgba(24, 144, 255, 0.1)',
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
})
// 商户分布图表
const merchantCtx = document.getElementById('merchantChart')
new Chart(merchantCtx, {
type: 'pie',
data: {
labels: ['餐饮', '零售', '娱乐', '其他'],
datasets: [{
data: [35, 25, 20, 20],
backgroundColor: ['#1890ff', '#52c41a', '#faad14', '#f5222d']
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
})
})
</script>
最佳实践
- 组件化开发:将界面拆分为可复用的组件,如布局、卡片、表格、表单等
- 样式一致性:使用统一的颜色方案和工具类,确保整个应用的视觉一致性
- 响应式设计:利用 Tailwind 的响应式类,确保在不同屏幕尺寸上的良好显示
- 性能优化:使用
content-auto等工具类优化渲染性能 - 代码规范:保持组件代码的清晰和可维护性
注意事项
- 确保安装了所有必要的依赖包
- 正确配置 Tailwind CSS 的内容路径,确保样式能够正确应用
- 在 Vue 3 项目中使用组合式 API 编写组件逻辑
- 对于图表等复杂功能,需要安装相应的库(如 Chart.js)
- 根据实际项目需求调整样式和组件
