1. Routing Configuration
1.1 Router Instance Setup
Initialize the Vue Router using the createRouter and createWebHistory functions. Define the routing mode and default scroll behavior to reset the position on navigation.
import { createRouter, createWebHistory } from 'vue-router'
import { staticRoutes } from './routes'
const appRouter = createRouter({
history: createWebHistory(),
routes: staticRoutes,
scrollBehavior() {
return { left: 0, top: 0 }
},
})
export default appRouter1.2 Route Definitions
Define the constant routes, including the authentication page, the main layout, and a catch-all route for 404 errors.
export const staticRoutes = [
{
path: '/auth',
component: () => import('@/views/auth/index.vue'),
name: 'Auth',
},
{
path: '/',
component: () => import('@/views/layout/index.vue'),
name: 'Layout',
redirect: '/home',
children: [
{
path: '/home',
component: () => import('@/views/home/index.vue'),
name: 'Home',
meta: { title: 'Dashboard' }
}
]
},
{
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'NotFound',
},
]2. Authentication Module
2.1 Login Interface Structure
Implement the login view using Element Plus components. Create a split layout with a background image on one side and the login form on the other.
<template>
<div class="login-wrapper">
<h1>Welcome</h1>
<h2>Enterprise System</h2>
Login
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { User, Lock } from '@element-plus/icons-vue'
import { useAuthStore } from '@/store/modules/auth'
import { useRouter } from 'vue-router'
const authStore = useAuthStore()
const router = useRouter()
const isLoading = ref(false)
const credentials = reactive({ account: 'admin', secret: '123456' })
const rules = {
account: [{ required: true, message: 'Account is required', trigger: 'blur' }],
secret: [{ required: true, message: 'Password is required', trigger: 'blur' }]
}
const handleLogin = async () => {
isLoading.value = true
try {
await authStore.login(credentials)
router.push('/')
} catch (error) {
console.error(error)
} finally {
isLoading.value = false
}
}
</script>2.2 Pinia Store for Authentication
Create a Pinia store to manage user state, token storage, and login/logout actions.
import { defineStore } from 'pinia'
import { loginReq, getUserInfoReq } from '@/api/auth'
import { SET_TOKEN, GET_TOKEN, REMOVE_TOKEN } from '@/utils/token'
interface AuthState {
token: string | null
userInfo: any
}
export const useAuthStore = defineStore('Auth', {
state: (): AuthState => ({
token: GET_TOKEN(),
userInfo: null
}),
actions: {
async login(data: any) {
const res = await loginReq(data)
if (res.code === 200) {
this.token = res.data
SET_TOKEN(res.data)
return 'ok'
} else {
return Promise.reject(new Error(res.message))
}
},
async getInfo() {
const res = await getUserInfoReq()
if (res.code === 200) {
this.userInfo = res.data
}
},
logout() {
this.token = ''
this.userInfo = null
REMOVE_TOKEN()
}
}
})3. Layout Architecture
3.1 Main Layout Component
Construct the layout shell consisting of a sidebar, top navigation bar, and main content area. Use CSS variables and fixed positioning for the tab bar.
<template>
<div class="layout-container">
<div class="layout-sidebar">
<Logo />
<Menu />
</div>
<div class="layout-tabbar">
<Breadcrumb />
<Settings />
</div>
<div class="layout-main">
<MainContent />
</div>
</div>
</template>
<style lang="scss" scoped>
.layout-container {
width: 100%;
height: 100vh;
.layout-sidebar {
width: $base-sidebar-width;
height: 100%;
background: $base-sidebar-bg;
}
.layout-tabbar {
position: fixed;
width: calc(100% - $base-sidebar-width);
height: $base-tabbar-height;
background: #fff;
top: 0;
left: $base-sidebar-width;
border-bottom: 1px solid #eee;
}
.layout-main {
position: absolute;
top: $base-tabbar-height;
left: $base-sidebar-width;
width: calc(100% - $base-sidebar-width);
height: calc(100vh - $base-tabbar-height);
padding: 20px;
overflow: auto;
background: #f0f2f5;
}
}
</style>3.2 Dynamic Menu Generation
Implement a recursive menu component that renders based on the route configuration. Use el-menu and handle route meta information for icons and titles.
<template>
<template v-for="item in menuRoutes" :key="item.path">
<component :is="item.meta.icon" />
<span>{{ item.meta.title }}</span>
<template #title>
<component :is="item.meta.icon" />
<span>{{ item.meta.title }}</span>
</template>
<Menu :menuRoutes="item.children" />
</template>
</template>
<script setup lang="ts">
defineProps(['menuRoutes'])
</script>
<script lang="ts">
export default { name: 'Menu' }
</script>4. Brand Management Module
4.1 Data Table and Pagination
Create a table view to display brand information. Implement pagination controls to handle large datasets.
<template>
Add Brand
<template #default="{ row }">
<img :src="row.logoUrl" style="width: 50px; height: 50px" />
</template>
<template #default="{ row }">
<template #reference>
</template>
</template>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { reqBrandList, reqAddOrUpdate, reqDeleteBrand } from '@/api/product/brand'
import type { BrandData } from '@/api/product/brand/type'
const pageNo = ref(1)
const limit = ref(3)
const total = ref(0)
const brandList = ref([])
const fetchBrands = async () => {
const res = await reqBrandList(pageNo.value, limit.value)
if (res.code === 200) {
brandList.value = res.data.records
total.value = res.data.total
}
}
onMounted(fetchBrands)
</script> 4.2 Add/Edit Brand Dialog
Implement a dialog form for adding and editing brands. Include form validation and image upload functionality.
<img v-if="brandParams.logoUrl" :src="brandParams.logoUrl" class="avatar" />
<Plus />
<template #footer>
Cancel
Confirm
</template>
5. Attribute Management Module
5.1 Category Selector
Create a global component for selecting three-level categories. Use cascading logic where selecting a parent category fetches its children.
<template>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import useCategoryStore from '@/store/modules/category'
const categoryStore = useCategoryStore()
onMounted(() => {
categoryStore.fetchC1()
})
const handleC1Change = () => {
categoryStore.c2Id = ''
categoryStore.c3Id = ''
categoryStore.c3Arr = []
categoryStore.fetchC2()
}
const handleC2Change = () => {
categoryStore.c3Id = ''
categoryStore.fetchC3()
}
</script>6. SPU and SKU Management
6.1 SPU Listing
Display a list of Standard Product Units. Include actions to add SKUs, edit the SPU, or delete it.
// API Call for SPU List
import { reqSpuList } from '@/api/product/spu'
const fetchSpuList = async (page = 1) => {
pageNo.value = page
const res = await reqSpuList(pageNo.value, pageSize.value, categoryStore.c3Id)
if (res.code === 200) {
spuRecords.value = res.data.records
total.value = res.data.total
}
}6.2 SKU Management
Implement the SKU list view with the ability to toggle the "On Sale" status. Use a drawer to display detailed SKU information.
<template>
<template #default="{ row }">
{{ row.isSale === 1 ? 'Off Sale' : 'On Sale' }}
Details
</template>
{{ currentSku.skuName }}
{{ currentSku.price }}
{{ currentSku.weight }}g
</template>