在完成AngularJS项目初始化与基础功能搭建后,实战应用的核心是“解决业务场景中的具体问题”——比如电商后台的商品数据表格需支持筛选、排序、批量操作,商品编辑表单需处理动态规格字段,不同角色用户需看到不同功能菜单。本文基于已搭建的电商后台框架,深入三大核心实战场景,提供“需求分析-技术实现-优化避坑”的全流程方案,帮你掌握AngularJS在实际业务中的应用技巧。
一、实战场景1:商品数据表格——支持筛选、排序与批量操作
商品管理是电商后台的核心模块,数据表格作为“数据展示与操作入口”,需满足“高效查询、便捷操作、性能稳定”三大需求。本场景将基于AngularJS Material的md-table组件,实现支持多条件筛选、字段排序、批量删除/上架的商品表格。
1. 需求拆解
数据展示:展示商品ID、名称、分类、价格、库存、状态(上架/下架)、操作按钮;
筛选功能:支持按商品名称模糊搜索、按分类下拉筛选、按状态(上架/下架)筛选;
排序功能:支持按价格(升序/降序)、库存(升序/降序)点击表头排序;
批量操作:支持勾选商品后批量删除、批量上架/下架;
性能优化:表格数据分页加载(默认10条/页),避免大数据量渲染卡顿。
2. 技术实现方案
(1)核心服务封装:商品数据服务(ProductService)
负责商品数据的获取、筛选、排序、批量操作,隔离数据逻辑与视图逻辑,便于后续维护。
// src/business/product/services/ProductService.js
import angular from 'angular';
export default angular.module('productModule.productService', [])
.factory('ProductService', ['$http', function($http) {
// 私有工具方法:处理数据筛选(多条件组合)
const filterProducts = (products, filters) => {
return products.filter(product => {
// 商品名称模糊匹配(忽略大小写)
const nameMatch = !filters.name ||
product.name.toLowerCase().includes(filters.name.toLowerCase());
// 分类精确匹配(未选择分类则不筛选)
const categoryMatch = !filters.categoryId ||
product.categoryId === filters.categoryId;
// 状态精确匹配(未选择状态则不筛选)
const statusMatch = filters.status === undefined ||
product.status === filters.status;
return nameMatch && categoryMatch && statusMatch;
});
};
// 私有工具方法:处理数据排序
const sortProducts = (products, sortConfig) => {
if (!sortConfig.field) return products; // 未指定排序字段则不排序
return [...products].sort((a, b) => {
// 按指定字段排序(数字类型直接比较,字符串类型按ASCII排序)
if (a[sortConfig.field] < b[sortConfig.field]) {
return sortConfig.direction === 'asc' ? -1 : 1;
}
if (a[sortConfig.field] > b[sortConfig.field]) {
return sortConfig.direction === 'asc' ? 1 : -1;
}
return 0;
});
};
return {
// 获取商品列表(支持分页、筛选、排序参数)
getProductList: (params = {}) => {
const { page = 1, pageSize = 10, filters = {}, sortConfig = {} } = params;
// 实际项目中调用后端API,此处模拟数据(实际需替换为$http请求)
return $http.get('/api/products', {
params: { page, pageSize, ...filters, sortField: sortConfig.field, sortDir: sortConfig.direction }
}).then(response => {
const { list: products, total } = response.data;
// 前端二次筛选与排序(若后端未支持则需处理,建议优先由后端实现)
const filtered = filterProducts(products, filters);
const sorted = sortProducts(filtered, sortConfig);
return {
list: sorted,
total: total, // 总条数(用于分页计算)
page: page,
pageSize: pageSize
};
});
},
// 批量操作商品(批量删除/上架/下架)
batchOperateProducts: (productIds, operateType) => {
const operateMap = {
delete: '/api/products/batch/delete',
上架: '/api/products/batch/up',
下架: '/api/products/batch/down'
};
const url = operateMap[operateType];
if (!url) return Promise.reject('不支持的操作类型');
zhiq.zhaopin.com/question/11491006
zhiq.zhaopin.com/question/11491008
zhiq.zhaopin.com/question/11491009
zhiq.zhaopin.com/question/11491010
zhiq.zhaopin.com/question/11491012
zhiq.zhaopin.com/question/11491014
zhiq.zhaopin.com/question/11494132
zhiq.zhaopin.com/question/11494191
zhiq.zhaopin.com/question/11494207
zhiq.zhaopin.com/question/11494212
zhiq.zhaopin.com/question/11494220
zhiq.zhaopin.com/question/11494226
zhiq.zhaopin.com/question/11494237
zhiq.zhaopin.com/question/11494249
zhiq.zhaopin.com/question/11494252
zhiq.zhaopin.com/question/11494261
zhiq.zhaopin.com/question/11494269
zhiq.zhaopin.com/question/11494295
zhiq.zhaopin.com/question/11494299
zhiq.zhaopin.com/question/11494303
return $http.post(url, { productIds });
},
// 单个商品操作(编辑/删除/上架/下架)
operateProduct: (productId, operateType, data = {}) => {
const operateMap = {
delete: { method: 'DELETE', url: `/api/products/${productId}` },
up: { method: 'PUT', url: `/api/products/${productId}/up` },
down: { method: 'PUT', url: `/api/products/${productId}/down` },
edit: { method: 'PUT', url: `/api/products/${productId}`, data: data }
};
const { method, url, data: reqData } = operateMap[operateType];
if (!method || !url) return Promise.reject('不支持的操作类型');
return $http({ method, url, data: reqData });
}
};
}])
.name;
(2)商品列表控制器(ProductListCtrl)
处理表格的视图交互逻辑,如筛选条件变更、排序切换、分页切换、批量操作触发。
// src/business/product/controllers/ProductListCtrl.js
import angular from 'angular';
import productListTemplate from '../views/productList.html';
export default angular.module('productModule.productListCtrl', [])
.controller('ProductListCtrl', ['$scope', '$state', 'ProductService', 'NotificationService', function($scope, $state, ProductService, NotificationService) {
// 1. 初始化状态:筛选条件、排序配置、分页配置、选中商品ID
$scope.filters = {
name: '', // 商品名称筛选
categoryId: '', // 分类筛选(默认空,即不筛选)
status: undefined // 状态筛选(undefined:不筛选,1:上架,0:下架)
};
$scope.sortConfig = {
field: 'price', // 默认排序字段(价格)
direction: 'asc' // 默认排序方向(升序)
};
$scope.pagination = {
page: 1, // 当前页
pageSize: 10, // 每页条数
total: 0 // 总条数(从接口获取)
};
$scope.productList = []; // 商品列表数据
$scope.selectedProductIds = []; // 选中的商品ID(用于批量操作)
$scope.isLoading = false; // 加载状态
// 2. 获取商品分类列表(用于筛选下拉框)
const getProductCategories = () => {
// 实际项目中调用后端API获取分类,此处模拟数据
$http.get('/api/categories').then(response => {
$scope.categories = response.data; // 格式:[{ id: 1, name: '服装' }, ...]
});
};
// 3. 加载商品列表数据(核心方法,支持筛选、排序、分页)
const loadProductList = () => {
$scope.isLoading = true;
ProductService.getProductList({
page: $scope.pagination.page,
pageSize: $scope.pagination.pageSize,
filters: $scope.filters,
sortConfig: $scope.sortConfig
}).then(result => {
$scope.productList = result.list;
$scope.pagination.total = result.total;
$scope.isLoading = false;
// 重置选中的商品ID(分页后选中状态清空)
$scope.selectedProductIds = [];
}).catch(error => {
$scope.isLoading = false;
NotificationService.error(error.data?.errorMsg || '加载商品列表失败');
});
};
// 4. 筛选条件变更:点击“搜索”按钮触发
$scope.onSearch = () => {
$scope.pagination.page = 1; // 筛选后重置为第一页
loadProductList();
};
// 5. 排序切换:点击表头触发(切换升序/降序)
$scope.onSort = (field) => {
if ($scope.sortConfig.field === field) {
// 同一字段:切换排序方向
$scope.sortConfig.direction = $scope.sortConfig.direction === 'asc' ? 'desc' : 'asc';
} else {
// 不同字段:默认升序
$scope.sortConfig.field = field;
$scope.sortConfig.direction = 'asc';
}
loadProductList();
};
// 6. 分页切换:页码或每页条数变更触发
$scope.onPageChange = (page) => {
$scope.pagination.page = page;
loadProductList();
};
// 7. 选中商品变更:勾选/取消勾选商品触发
$scope.onSelectProduct = (productId, isSelected) => {
if (isSelected) {
// 勾选:添加到选中列表
$scope.selectedProductIds.push(productId);
} else {
// 取消勾选:从选中列表移除
$scope.selectedProductIds = $scope.selectedProductIds.filter(id => id !== productId);
}
};
// 8. 批量操作商品:触发批量删除/上架/下架
$scope.batchOperate = (operateType) => {
if ($scope.selectedProductIds.length === 0) {
NotificationService.warning('请先选择要操作的商品');
return;
}
// 确认操作(使用之前封装的modal指令)
$scope.batchOperateModal = {
show: true,
config: {
title: `批量${operateType}商品`,
content: `确定要${operateType}选中的${$scope.selectedProductIds.length}个商品吗?`,
type: 'confirm',
onButtonClick: (result) => {
if (result.buttonType === 'confirm') {
// 确认操作:调用服务批量处理
ProductService.batchOperateProducts($scope.selectedProductIds, operateType)
.then(() => {
NotificationService.success(`批量${operateType}商品成功`);
loadProductList(); // 重新加载列表
})
.catch(error => {
NotificationService.error(error.data?.errorMsg || `批量${operateType}商品失败`);
});
}
$scope.batchOperateModal.show = false;
}
}
};
};
// 9. 单个商品操作:编辑/删除/上架/下架
$scope.operateProduct = (productId, operateType, productData = {}) => {
if (operateType === 'edit') {
// 编辑操作:跳转到编辑页(携带商品ID)
$state.go('app.product.edit', { productId: productId });
return;
}
// 其他操作(删除/上架/下架):确认后执行
const operateName = { delete: '删除', up: '上架', down: '下架' }[operateType];
NotificationService.confirm(`确定要${operateName}该商品吗?`, () => {
ProductService.operateProduct(productId, operateType, productData)
.then(() => {
NotificationService.success(`${operateName}商品成功`);
loadProductList(); // 重新加载列表
})
.catch(error => {
NotificationService.error(error.data?.errorMsg || `${operateName}商品失败`);
});
});
};
// 10. 初始化:加载分类与商品列表
const init = () => {
getProductCategories();
loadProductList();
};
init();
}])
// 配置商品列表路由
.config(['$stateProvider', function($stateProvider) {
$stateProvider.state('app.product.list', {
url: '/list',
template: productListTemplate,
controller: 'ProductListCtrl',
data: {
requireLogin: true, // 需要登录权限
menuName: '商品管理' // 用于导航栏高亮
}
});
}])
.name;
(3)商品列表视图(productList.html)
基于AngularJS Material的md-table实现表格布局,集成筛选、排序、分页组件。
<md-card>
<!-- 卡片头部:标题 + 筛选区域 -->
<md-card-header>
<md-card-header-text>
<h2>商品列表</h2>
</md-card-header-text>
<md-card-actions layout="row" layout-align="end center">
<!-- 新增商品按钮 -->
<md-button class="md-primary md-raised" ng-click="$state.go('app.product.add')">
<i class="fa fa-plus"></i> 新增商品
</md-button>
</md-card-actions>
</md-card-header>
<!-- 筛选区域 -->
<md-card-content>
<div layout="row" layout-wrap gap="16px" class="filter-container">
<!-- 商品名称筛选 -->
<md-input-container flex="25">
<label>商品名称</label>
<input type="text" ng-model="filters.name" placeholder="请输入商品名称搜索">
</md-input-container>
<!-- 分类筛选 -->
<md-input-container flex="25">
<label>商品分类</label>
<md-select ng-model="filters.categoryId" placeholder="请选择分类">
<md-option value="">全部分类</md-option>
<md-option ng-repeat="category in categories" value="{{category.id}}">
{{category.name}}
</md-option>
</md-select>
</md-input-container>
<!-- 状态筛选 -->
<md-input-container flex="25">
<label>商品状态</label>
<md-select ng-model="filters.status" placeholder="请选择状态">
<md-option value="">全部状态</md-option>
<md-option value="1">上架</md-option>
<md-option value="0">下架</md-option>
</md-select>
</md-input-container>
<!-- 搜索按钮 -->
<md-input-container flex="20" layout-align="end center">
<md-button class="md-primary md-raised" ng-click="onSearch()">
<i class="fa fa-search"></i> 搜索
</md-button>
</md-input-container>
</div>
<!-- 批量操作按钮 -->
<div class="batch-operate-container" ng-if="productList.length > 0">
<md-button class="md-warn" ng-click="batchOperate('delete')" ng-disabled="selectedProductIds.length === 0">
<i class="fa fa-trash"></i> 批量删除
</md-button>
<md-button class="md-primary" ng-click="batchOperate('上架')" ng-disabled="selectedProductIds.length === 0">
<i class="fa fa-arrow-up"></i> 批量上架
</md-button>
<md-button class="md-accent" ng-click="batchOperate('下架')" ng-disabled="selectedProductIds.length === 0">
<i class="fa fa-arrow-down"></i> 批量下架
</md-button>
</div>
<!-- 商品表格 -->
<md-table-container ng-if="productList.length > 0">
<table md-table md-row-select="false" ng-model="selectedProductIds">
<!-- 复选框列 -->
<thead md-head>
<tr md-row>
<th md-column width="50px">
<md-checkbox ng-model="selectAll" ng-click="selectAllProducts(selectAll)"></md-checkbox>
</th>
<!-- 表头:支持点击排序 -->
<th md-column md-sort-header="id" ng-click="onSort('id')">商品ID</th>
<th md-column md-sort-header="name" ng-click="onSort('name')">商品名称</th>
<th md-column md-sort-header="categoryName" ng-click="onSort('categoryName')">分类</th>
<th md-column md-sort-header="price" ng-click="onSort('price')">
</doubaocanvas>