Compare commits

..

62 Commits

Author SHA1 Message Date
py4fbma29 22f045283f Merge pull request '合并' (#12) from develop into main
8 months ago
dwj 1dbbb0f638 Merge branch 'main' of https://bdgit.educoder.net/py4fbma29/mall-admin
8 months ago
dwj 9f8543ddc0 1
8 months ago
py4fbma29 9910f12c6c Merge pull request '合并' (#11) from branch_cwy into main
8 months ago
py4fbma29 c5c92c1f3c Merge pull request '1' (#10) from develop into main
8 months ago
dwj 2c34da5b38 1
8 months ago
py4fbma29 f5104168cb Merge pull request '1' (#9) from develop into main
8 months ago
dwj 13e00e2c52 Merge branch 'main' of https://bdgit.educoder.net/py4fbma29/mall-admin
8 months ago
dwj f082787c68 1
8 months ago
cwy 670a00aacf 111
8 months ago
cwy efcacb5985 111
8 months ago
cwy b50b497f91 111
8 months ago
py4fbma29 f0664c6070 Merge pull request '合并' (#8) from branch_cwy into main
8 months ago
py4fbma29 e5263c4c5e Merge pull request '合并' (#7) from develop into main
8 months ago
dwj c93faf8e2d .
8 months ago
py4fbma29 31568ac144 Merge pull request '.' (#6) from develop into main
8 months ago
dwj 3f3d382407 .
8 months ago
py4fbma29 648ef08f1f Merge pull request '合并' (#5) from branch_lyj into main
8 months ago
dwj 7fea894a2f .
8 months ago
dwj d72f9a90fd Merge branch 'main' of https://bdgit.educoder.net/py4fbma29/mall-admin
8 months ago
dwj 2d5489f2d1 .
8 months ago
py4fbma29 5e78d9fccd 合并
8 months ago
wangzhuo 54cf3faf5c wangzhuo
8 months ago
wangzhuo e81e35e6e9 wangzhuo
8 months ago
wangzhuo 5450926f5c wangzhuo
8 months ago
wangzhuo a764ea1d8e wangzhuo
8 months ago
cwy 2737b1315a 111
8 months ago
cwy f8baf3d847 111
8 months ago
dwj 92e1c03885 Merge branch 'main' of https://bdgit.educoder.net/py4fbma29/mall-admin
8 months ago
dwj de5b79a3df .
8 months ago
py4fbma29 0908946eba Merge pull request '注释' (#3) from develop into main
8 months ago
dwj 685d3ec7f1 注释
8 months ago
dwj f603c1038c 12345
8 months ago
dwj 6956784329 123
8 months ago
wangzhuo 2e151dc4d6 wangzhuo
8 months ago
wangzhuo 54e246ab37 wangzhuo
8 months ago
wangzhuo 0470e3576e wangzhuo
8 months ago
wangzhuo 416d6791e0 wangzhuo
8 months ago
wangzhuo 9ff756069d wangzhuo
8 months ago
wangzhuo f379e363b3 wangzhuo
8 months ago
wangzhuo 19da99fd06 wangzhuo
8 months ago
wangzhuo 81c22d19a5 wangzhuo
8 months ago
wangzhuo 36e8585bb4 wangzhuo
8 months ago
wangzhuo d4e953c6a2 wangzhuo
8 months ago
wangzhuo a08e37cfb5 wangzhuo
8 months ago
wangzhuo 4943aa300c wangzhuo
8 months ago
wangzhuo 007922df3f wangzhuo
8 months ago
cwy d27ed4e549 113
8 months ago
cwy 1e297f6594 Merge branch 'main' of https://bdgit.educoder.net/py4fbma29/mall-admin
8 months ago
cwy d47e960b34 113
8 months ago
pog6ch8ae 5e51d3cdb2 Merge pull request '测试' (#2) from branch_cwy into main
8 months ago
cwy fa3736d39b 119
8 months ago
cwy 0cc4c7b440 118
8 months ago
cwy 0fd8d33c0e 117
8 months ago
cwy bf8b710171 116
8 months ago
cwy a929473bfa 115
8 months ago
cwy 9311154b5e 114
8 months ago
cwy 12b1a5a49c 113
8 months ago
cwy 73e0e034a8 112
8 months ago
cwy e65de8a5d2 112
8 months ago
cwy 6f77f0cd3b 111
8 months ago
dwj e26af308cc 111
8 months ago

@ -1,7 +1,7 @@
# mall-admin-web # mall-admin-web
<p> <p>
<a href="#公众号"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-macrozheng-blue.svg" alt="公众号"></a>
<a href="#公众号"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E4%BA%A4%E6%B5%81-%E5%BE%AE%E4%BF%A1%E7%BE%A4-2BA245.svg" alt="交流"></a>
<a href="https://github.com/macrozheng/mall"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%90%8E%E5%8F%B0%E9%A1%B9%E7%9B%AE-mall-blue.svg" alt="后台项目"></a> <a href="https://github.com/macrozheng/mall"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%90%8E%E5%8F%B0%E9%A1%B9%E7%9B%AE-mall-blue.svg" alt="后台项目"></a>
<a href="https://github.com/macrozheng/mall-swarm"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/Cloud%E7%89%88%E6%9C%AC-mall--swarm-brightgreen.svg" alt="SpringCloud版本"></a> <a href="https://github.com/macrozheng/mall-swarm"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/Cloud%E7%89%88%E6%9C%AC-mall--swarm-brightgreen.svg" alt="SpringCloud版本"></a>
<a href="https://gitee.com/macrozheng/mall-admin-web"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E7%A0%81%E4%BA%91-%E9%A1%B9%E7%9B%AE%E5%9C%B0%E5%9D%80-orange.svg" alt="码云"></a> <a href="https://gitee.com/macrozheng/mall-admin-web"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E7%A0%81%E4%BA%91-%E9%A1%B9%E7%9B%AE%E5%9C%B0%E5%9D%80-orange.svg" alt="码云"></a>
@ -9,15 +9,14 @@
## 前言 ## 前言
该项目为前后端分离项目的前端部分,后端项目`mall`地址:[传送门](https://github.com/macrozheng/mall) 。 该项目为前后端分离项目的前端部分
## 项目介绍 ## 项目介绍
`mall-admin-web`是一个电商后台管理系统的前端项目基于Vue+Element实现。主要包括商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等功能。 `mall-admin-web`是一个电商后台管理系统的前端项目基于Vue+Element实现。主要包括商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等功能。
### 项目演示 ### 项目演示
项目在线演示地址:[https://www.macrozheng.com/admin/](https://www.macrozheng.com/admin/) 项目演示地址:[https://www.macrozheng.com/admin/](https://www.macrozheng.com/admin/)
![后台管理系统功能演示](http://img.macrozheng.com/mall/project/mall_admin_show.png) ![后台管理系统功能演示](http://img.macrozheng.com/mall/project/mall_admin_show.png)
@ -68,14 +67,6 @@ src -- 源码目录
- 具体部署过程请参考:[mall前端项目的安装与部署](https://www.macrozheng.com/mall/deploy/mall_deploy_web.html) - 具体部署过程请参考:[mall前端项目的安装与部署](https://www.macrozheng.com/mall/deploy/mall_deploy_web.html)
- 前端自动化部署请参考:[使用Jenkins一键打包部署前端应用就是这么6](https://www.macrozheng.com/mall/reference/jenkins_vue.html) - 前端自动化部署请参考:[使用Jenkins一键打包部署前端应用就是这么6](https://www.macrozheng.com/mall/reference/jenkins_vue.html)
## 公众号
学习不走弯路,关注公众号「**macrozheng**」,回复「**学习路线**」获取mall项目专属学习路线
加微信群交流,公众号后台回复「**加群**」即可。
![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg)
## 许可证 ## 许可证
[Apache License 2.0](https://github.com/macrozheng/mall-admin-web/blob/master/LICENSE) [Apache License 2.0](https://github.com/macrozheng/mall-admin-web/blob/master/LICENSE)

@ -2,7 +2,6 @@
require('./check-versions')() require('./check-versions')()
process.env.NODE_ENV = 'production' process.env.NODE_ENV = 'production'
const ora = require('ora') const ora = require('ora')
const rm = require('rimraf') const rm = require('rimraf')
const path = require('path') const path = require('path')
@ -10,10 +9,8 @@ const chalk = require('chalk')
const webpack = require('webpack') const webpack = require('webpack')
const config = require('../config') const config = require('../config')
const webpackConfig = require('./webpack.prod.conf') const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...') const spinner = ora('building for production...')
spinner.start() spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err if (err) throw err
webpack(webpackConfig, (err, stats) => { webpack(webpackConfig, (err, stats) => {

@ -8,7 +8,6 @@ exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production' const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory ? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory : config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path) return path.posix.join(assetsSubDirectory, _path)
} }

@ -16,6 +16,8 @@ import java.util.stream.Collectors;
* i11111 * i11111
* dsdhsjhfjs * dsdhsjhfjs
* fdjkf * fdjkf
* 1111111
* hjhfsj
*/ */
public class AdminUserDetails implements UserDetails { public class AdminUserDetails implements UserDetails {
//后台用户 //后台用户

@ -9,7 +9,7 @@
<name>mall-security</name> <name>mall-security</name>
<description>mall-security project for mall</description> <description>mall-security project for mall</description>
//zzzzzzz
<parent> <parent>
<groupId>com.macro.mall</groupId> <groupId>com.macro.mall</groupId>
<artifactId>mall</artifactId> <artifactId>mall</artifactId>

@ -14,7 +14,7 @@ import org.springframework.stereotype.Component;
import java.lang.reflect.Method; import java.lang.reflect.Method;
/** /**123456
* RedisRedis * RedisRedis
* Created by macro on 2020/3/17. * Created by macro on 2020/3/17.
*/ */

@ -1,54 +1,70 @@
// 引入自定义的请求工具模块
import request from '@/utils/request' import request from '@/utils/request'
// 获取品牌列表
export function fetchList(params) { export function fetchList(params) {
// 调用请求方法发送GET请求到 '/brand/list',并将参数作为请求的查询参数
return request({ return request({
url:'/brand/list', url: '/brand/list', // 请求的API接口地址
method:'get', method: 'get', // 请求方法GET
params:params params: params // 请求参数将被附加到URL的查询字符串中
}) })
} }
//dwj1111111
// 创建新品牌
export function createBrand(data) { export function createBrand(data) {
// 调用请求方法发送POST请求到 '/brand/create',请求体包含品牌数据
return request({ return request({
url:'/brand/create', url: '/brand/create', // 请求的API接口地址
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体数据,包含品牌的详细信息
}) })
} }
// 更新品牌的展示状态
export function updateShowStatus(data) { export function updateShowStatus(data) {
// 调用请求方法发送POST请求到 '/brand/update/showStatus',请求体包含展示状态的更新数据
return request({ return request({
url:'/brand/update/showStatus', url: '/brand/update/showStatus', // 请求的API接口地址
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体数据,包含要更新的展示状态信息
}) })
} }
// 更新品牌的工厂状态
export function updateFactoryStatus(data) { export function updateFactoryStatus(data) {
// 调用请求方法发送POST请求到 '/brand/update/factoryStatus',请求体包含工厂状态的更新数据
return request({ return request({
url:'/brand/update/factoryStatus', url: '/brand/update/factoryStatus', // 请求的API接口地址
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体数据,包含要更新的工厂状态信息
}) })
} }
// 删除品牌
export function deleteBrand(id) { export function deleteBrand(id) {
// 调用请求方法发送GET请求到 '/brand/delete/{id}'{id}为品牌的唯一标识
return request({ return request({
url:'/brand/delete/'+id, url: '/brand/delete/' + id, // 请求的API接口地址{id}动态插入
method:'get', method: 'get', // 请求方法GET
}) })
} }
// 获取单个品牌的详细信息
export function getBrand(id) { export function getBrand(id) {
// 调用请求方法发送GET请求到 '/brand/{id}'{id}为品牌唯一标识
return request({ return request({
url:'/brand/'+id, url: '/brand/' + id, // 请求的API接口地址{id}动态插入
method:'get', method: 'get', // 请求方法GET
}) })
} }
export function updateBrand(id,data) { // 更新品牌的详细信息
export function updateBrand(id, data) {
// 调用请求方法发送POST请求到 '/brand/update/{id}',请求体包含品牌的更新数据
return request({ return request({
url:'/brand/update/'+id, url: '/brand/update/' + id, // 请求的API接口地址{id}动态插入
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体数据,包含要更新的品牌详细信息
}) })
} }

@ -1,7 +1,27 @@
// 导入封装好的请求工具用于发起HTTP请求
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取公司地址列表数据的函数
*
* 该函数通过发送一个 GET 请求到指定的后端接口
* 以获取公司地址列表数据数据返回后可以用于前端页面渲染
*
* @returns {Promise} - 返回一个Promise对象包含接口返回的数据
*
* 用法示例
* fetchList().then(response => {
* console.log(response); // 处理接口返回的数据
* }).catch(error => {
* console.error(error); // 处理错误
* });
*/
export function fetchList() { export function fetchList() {
return request({ return request({
url:'/companyAddress/list', // 接口的URL路径指向公司地址列表的后端接口
method:'get' url: '/companyAddress/list',
// 请求方法:使用 HTTP GET 方法获取数据
method: 'get'
}) })
} }

@ -1,38 +1,106 @@
// 导入封装的 HTTP 请求工具,用于与后端接口通信
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取优惠券列表
*
* 该函数通过 GET 请求获取优惠券列表并支持通过参数进行查询筛选
*
* @param {Object} params - 查询参数例如分页筛选条件等
* @returns {Promise} - 返回一个Promise对象包含接口返回的数据
*
* 用法示例
* fetchList({ pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response);
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url:'/coupon/list', url: '/coupon/list', // 接口URL路径获取优惠券列表
method:'get', method: 'get', // 请求方法GET
params:params params: params // 查询参数通过URL参数传递
}) })
} }
/**
* createCoupon - 创建新的优惠券
*
* 该函数通过 POST 请求向后端提交新优惠券的数据
*
* @param {Object} data - 优惠券的详细数据包含名称折扣等字段
* @returns {Promise} - 返回一个Promise对象包含接口返回的数据
*
* 用法示例
* createCoupon({ name: 'Spring Sale', discount: 20 }).then(response => {
* console.log(response);
* });
*/
export function createCoupon(data) { export function createCoupon(data) {
return request({ return request({
url:'/coupon/create', url: '/coupon/create', // 接口URL路径创建优惠券
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含优惠券数据
}) })
} }
/**
* getCoupon - 获取单个优惠券的详情
*
* 该函数通过 GET 请求获取指定ID的优惠券详细信息
*
* @param {number|string} id - 优惠券的唯一标识ID
* @returns {Promise} - 返回一个Promise对象包含优惠券的详细数据
*
* 用法示例
* getCoupon(1).then(response => {
* console.log(response);
* });
*/
export function getCoupon(id) { export function getCoupon(id) {
return request({ return request({
url:'/coupon/'+id, url: '/coupon/' + id, // 接口URL路径根据ID获取优惠券详情
method:'get', method: 'get' // 请求方法GET
}) })
} }
export function updateCoupon(id,data) { /**
* updateCoupon - 更新指定优惠券的信息
*
* 该函数通过 POST 请求提交修改后的优惠券数据更新指定ID的优惠券
*
* @param {number|string} id - 优惠券的唯一标识ID
* @param {Object} data - 修改后的优惠券数据
* @returns {Promise} - 返回一个Promise对象包含操作结果
*
* 用法示例
* updateCoupon(1, { name: 'Updated Sale', discount: 25 }).then(response => {
* console.log(response);
* });
*/
export function updateCoupon(id, data) {
return request({ return request({
url:'/coupon/update/'+id, url: '/coupon/update/' + id, // 接口URL路径更新指定ID的优惠券
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含修改后的数据
}) })
} }
/**
* deleteCoupon - 删除指定优惠券
*
* 该函数通过 POST 请求删除指定ID的优惠券
*
* @param {number|string} id - 优惠券的唯一标识ID
* @returns {Promise} - 返回一个Promise对象包含操作结果
*
* 用法示例
* deleteCoupon(1).then(response => {
* console.log(response);
* });
*/
export function deleteCoupon(id) { export function deleteCoupon(id) {
return request({ return request({
url:'/coupon/delete/'+id, url: '/coupon/delete/' + id, // 接口URL路径删除指定ID的优惠券
method:'post', method: 'post' // 请求方法POST
}) })
} }

@ -1,8 +1,27 @@
// 导入封装的 HTTP 请求工具,用于发送 HTTP 请求
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取优惠券历史记录列表
*
* 该函数通过 GET 请求获取优惠券使用历史的记录列表
* 可以通过传入查询参数如分页筛选条件等来筛选数据
*
* @param {Object} params - 查询参数包括分页信息筛选条件等
* 示例参数{ pageNum: 1, pageSize: 10, couponId: 1001 }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchList({ pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response); // 处理接口返回的数据
* }).catch(error => {
* console.error('获取数据失败:', error); // 处理请求错误
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url:'/couponHistory/list', url: '/couponHistory/list', // 接口URL路径获取优惠券历史记录列表
method:'get', method: 'get', // 请求方法GET
params:params params: params // 查询参数,通过 URL 参数传递
}) })
} }

@ -1,35 +1,109 @@
// 导入封装的 HTTP 请求工具,用于发送 HTTP 请求
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取限时购活动列表
*
* 该函数通过 GET 请求获取限时购活动的列表数据并支持通过参数进行查询筛选
*
* @param {Object} params - 查询参数例如分页筛选条件等
* 示例{ pageNum: 1, pageSize: 10 }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchList({ pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response);
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url:'/flash/list', url: '/flash/list', // 接口URL获取限时购活动列表
method:'get', method: 'get', // 请求方法GET
params:params params: params // 查询参数,通过 URL 查询字符串传递
}) })
} }
export function updateStatus(id,params) {
/**
* updateStatus - 更新限时购活动状态
*
* 该函数通过 POST 请求更新指定限时购活动的状态
*
* @param {number|string} id - 限时购活动的唯一标识ID
* @param {Object} params - 状态更新参数例如 { status: 1 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateStatus(1, { status: 1 }).then(response => {
* console.log(response);
* });
*/
export function updateStatus(id, params) {
return request({ return request({
url:'/flash/update/status/'+id, url: '/flash/update/status/' + id, // 接口URL更新指定活动状态
method:'post', method: 'post', // 请求方法POST
params:params params: params // 状态参数,通过 URL 查询字符串传递
}) })
} }
/**
* deleteFlash - 删除指定的限时购活动
*
* 该函数通过 POST 请求删除指定ID的限时购活动
*
* @param {number|string} id - 限时购活动的唯一标识ID
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* deleteFlash(1).then(response => {
* console.log(response);
* });
*/
export function deleteFlash(id) { export function deleteFlash(id) {
return request({ return request({
url:'/flash/delete/'+id, url: '/flash/delete/' + id, // 接口URL删除指定活动
method:'post' method: 'post' // 请求方法POST
}) })
} }
/**
* createFlash - 创建新的限时购活动
*
* 该函数通过 POST 请求向后端提交新的限时购活动数据
*
* @param {Object} data - 活动的详细数据例如 { name: 'Flash Sale', startTime: '2024-06-01' }
* @returns {Promise} - 返回一个 Promise 对象包含创建结果
*
* 用法示例
* createFlash({ name: 'Summer Flash Sale', startTime: '2024-06-01' }).then(response => {
* console.log(response);
* });
*/
export function createFlash(data) { export function createFlash(data) {
return request({ return request({
url:'/flash/create', url: '/flash/create', // 接口URL创建限时购活动
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含活动的详细数据
}) })
} }
export function updateFlash(id,data) {
/**
* updateFlash - 更新指定的限时购活动
*
* 该函数通过 POST 请求提交更新后的活动数据更新指定ID的限时购活动
*
* @param {number|string} id - 限时购活动的唯一标识ID
* @param {Object} data - 更新后的活动数据
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateFlash(1, { name: 'Updated Flash Sale', startTime: '2024-06-15' }).then(response => {
* console.log(response);
* });
*/
export function updateFlash(id, data) {
return request({ return request({
url:'/flash/update/'+id, url: '/flash/update/' + id, // 接口URL更新指定ID的活动
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含更新后的活动数据
}) })
} }

@ -1,28 +1,88 @@
// 导入封装的 HTTP 请求工具,用于与后端 API 进行通信
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取限时购商品关联列表
*
* 该函数通过 GET 请求获取限时购活动与商品的关联列表
* 支持通过查询参数进行筛选和分页
*
* @param {Object} params - 查询参数例如分页和过滤条件
* 示例{ flashId: 1, pageNum: 1, pageSize: 10 }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchList({ flashId: 1, pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response);
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url:'/flashProductRelation/list', url: '/flashProductRelation/list', // 接口URL获取关联列表
method:'get', method: 'get', // 请求方法GET
params:params params: params // 查询参数,通过 URL 传递
}) })
} }
/**
* createFlashProductRelation - 创建新的限时购商品关联
*
* 该函数通过 POST 请求提交限时购活动与商品的关联数据
*
* @param {Object} data - 关联数据例如 { flashId: 1, productId: 100, sort: 1 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* createFlashProductRelation({ flashId: 1, productId: 100, sort: 1 }).then(response => {
* console.log(response);
* });
*/
export function createFlashProductRelation(data) { export function createFlashProductRelation(data) {
return request({ return request({
url:'/flashProductRelation/create', url: '/flashProductRelation/create', // 接口URL创建关联
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含关联数据
}) })
} }
/**
* deleteFlashProductRelation - 删除指定的限时购商品关联
*
* 该函数通过 POST 请求删除指定 ID 的限时购商品关联数据
*
* @param {number|string} id - 限时购商品关联的唯一标识 ID
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* deleteFlashProductRelation(1).then(response => {
* console.log(response);
* });
*/
export function deleteFlashProductRelation(id) { export function deleteFlashProductRelation(id) {
return request({ return request({
url:'/flashProductRelation/delete/'+id, url: '/flashProductRelation/delete/' + id, // 接口URL删除关联
method:'post' method: 'post' // 请求方法POST
}) })
} }
export function updateFlashProductRelation(id,data) {
/**
* updateFlashProductRelation - 更新指定的限时购商品关联数据
*
* 该函数通过 POST 请求提交更新后的限时购商品关联数据
*
* @param {number|string} id - 限时购商品关联的唯一标识 ID
* @param {Object} data - 更新后的关联数据例如 { sort: 2 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateFlashProductRelation(1, { sort: 2 }).then(response => {
* console.log(response);
* });
*/
export function updateFlashProductRelation(id, data) {
return request({ return request({
url:'/flashProductRelation/update/'+id, url: '/flashProductRelation/update/' + id, // 接口URL更新关联
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含更新数据
}) })
} }

@ -1,48 +1,131 @@
// 导入封装的 HTTP 请求工具,用于与后端 API 进行通信
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取限时购场次列表
*
* 通过 GET 请求获取限时购场次的列表数据支持查询参数筛选结果
*
* @param {Object} params - 查询参数例如分页信息等
* 示例{ pageNum: 1, pageSize: 10 }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchList({ pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response);
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url: '/flashSession/list', url: '/flashSession/list', // 接口URL获取限时购场次列表
method: 'get', method: 'get', // 请求方法GET
params: params params: params // 查询参数,通过 URL 传递
}) })
} }
/**
* fetchSelectList - 获取可选择的限时购场次列表
*
* 通过 GET 请求获取简化版的限时购场次数据用于下拉选择框等场景
*
* @param {Object} params - 查询参数例如过滤条件等
* 示例{ flashId: 1 }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchSelectList({ flashId: 1 }).then(response => {
* console.log(response);
* });
*/
export function fetchSelectList(params) { export function fetchSelectList(params) {
return request({ return request({
url: '/flashSession/selectList', url: '/flashSession/selectList', // 接口URL获取可选的场次列表
method: 'get', method: 'get', // 请求方法GET
params: params params: params // 查询参数
}) })
} }
/**
* updateStatus - 更新指定场次的状态
*
* 通过 POST 请求更新场次的状态例如启用或禁用状态
*
* @param {number|string} id - 限时购场次的唯一标识ID
* @param {Object} params - 状态参数例如 { status: 1 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateStatus(1, { status: 1 }).then(response => {
* console.log(response);
* });
*/
export function updateStatus(id, params) { export function updateStatus(id, params) {
return request({ return request({
url: '/flashSession/update/status/' + id, url: '/flashSession/update/status/' + id, // 接口URL更新场次状态
method: 'post', method: 'post', // 请求方法POST
params: params params: params // 状态参数,通过 URL 查询字符串传递
}) })
} }
/**
* deleteSession - 删除指定的限时购场次
*
* 通过 POST 请求删除指定 ID 的限时购场次
*
* @param {number|string} id - 限时购场次的唯一标识ID
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* deleteSession(1).then(response => {
* console.log(response);
* });
*/
export function deleteSession(id) { export function deleteSession(id) {
return request({ return request({
url: '/flashSession/delete/' + id, url: '/flashSession/delete/' + id, // 接口URL删除场次
method: 'post' method: 'post' // 请求方法POST
}) })
} }
/**
* createSession - 创建新的限时购场次
*
* 通过 POST 请求提交新的场次数据创建限时购场次
*
* @param {Object} data - 场次的详细数据例如 { name: '上午场', startTime: '08:00', endTime: '12:00' }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* createSession({ name: '上午场', startTime: '08:00', endTime: '12:00' }).then(response => {
* console.log(response);
* });
*/
export function createSession(data) { export function createSession(data) {
return request({ return request({
url: '/flashSession/create', url: '/flashSession/create', // 接口URL创建场次
method: 'post', method: 'post', // 请求方法POST
data: data data: data // 请求体,包含场次数据
}) })
} }
/**
* updateSession - 更新指定的限时购场次数据
*
* 通过 POST 请求提交更新后的场次数据更新指定 ID 的场次信息
*
* @param {number|string} id - 限时购场次的唯一标识ID
* @param {Object} data - 更新后的场次数据例如 { name: '下午场', startTime: '13:00', endTime: '18:00' }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateSession(1, { name: '下午场', startTime: '13:00', endTime: '18:00' }).then(response => {
* console.log(response);
* });
*/
export function updateSession(id, data) { export function updateSession(id, data) {
return request({ return request({
url: '/flashSession/update/' + id, url: '/flashSession/update/' + id, // 接口URL更新场次信息
method: 'post', method: 'post', // 请求方法POST
data: data data: data // 请求体,包含更新数据
}) })
} }

@ -1,43 +1,130 @@
// 导入封装的 HTTP 请求工具,用于与后端 API 进行通信
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取首页广告列表
*
* 通过 GET 请求获取首页广告列表支持查询参数进行筛选或分页
*
* @param {Object} params - 查询参数例如分页和过滤条件
* 示例{ pageNum: 1, pageSize: 10 }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchList({ pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response);
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url:'/home/advertise/list', url: '/home/advertise/list', // 接口URL获取首页广告列表
method:'get', method: 'get', // 请求方法GET
params:params params: params // 查询参数,通过 URL 传递
}) })
} }
export function updateStatus(id,params) {
/**
* updateStatus - 更新指定广告的状态
*
* 通过 POST 请求更新首页广告的状态启用/禁用
*
* @param {number|string} id - 广告的唯一标识ID
* @param {Object} params - 状态参数例如 { status: 1 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateStatus(1, { status: 1 }).then(response => {
* console.log(response);
* });
*/
export function updateStatus(id, params) {
return request({ return request({
url:'/home/advertise/update/status/'+id, url: '/home/advertise/update/status/' + id, // 接口URL更新广告状态
method:'post', method: 'post', // 请求方法POST
params:params params: params // 状态参数,通过 URL 查询字符串传递
}) })
} }
/**
* deleteHomeAdvertise - 批量删除首页广告
*
* 通过 POST 请求删除多个广告记录
*
* @param {Array<number|string>} data - 广告ID数组例如 [1, 2, 3]
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* deleteHomeAdvertise([1, 2, 3]).then(response => {
* console.log(response);
* });
*/
export function deleteHomeAdvertise(data) { export function deleteHomeAdvertise(data) {
return request({ return request({
url:'/home/advertise/delete', url: '/home/advertise/delete', // 接口URL批量删除广告
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体包含广告ID数组
}) })
} }
/**
* createHomeAdvertise - 创建新的首页广告
*
* 通过 POST 请求提交新的广告数据创建首页广告
*
* @param {Object} data - 广告的详细数据例如 { name: '新广告', type: 1, pic: 'url' }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* createHomeAdvertise({ name: '新广告', type: 1 }).then(response => {
* console.log(response);
* });
*/
export function createHomeAdvertise(data) { export function createHomeAdvertise(data) {
return request({ return request({
url:'/home/advertise/create', url: '/home/advertise/create', // 接口URL创建广告
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含广告数据
}) })
} }
/**
* getHomeAdvertise - 获取指定广告详情
*
* 通过 GET 请求获取单个广告的详细信息
*
* @param {number|string} id - 广告的唯一标识ID
* @returns {Promise} - 返回一个 Promise 对象包含广告详情数据
*
* 用法示例
* getHomeAdvertise(1).then(response => {
* console.log(response);
* });
*/
export function getHomeAdvertise(id) { export function getHomeAdvertise(id) {
return request({ return request({
url:'/home/advertise/'+id, url: '/home/advertise/' + id, // 接口URL获取广告详情
method:'get', method: 'get' // 请求方法GET
}) })
} }
export function updateHomeAdvertise(id,data) { /**
* updateHomeAdvertise - 更新指定广告的信息
*
* 通过 POST 请求提交更新后的广告数据更新指定广告的信息
*
* @param {number|string} id - 广告的唯一标识ID
* @param {Object} data - 更新后的广告数据例如 { name: '更新广告', type: 2 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateHomeAdvertise(1, { name: '更新广告', type: 2 }).then(response => {
* console.log(response);
* });
*/
export function updateHomeAdvertise(id, data) {
return request({ return request({
url:'/home/advertise/update/'+id, url: '/home/advertise/update/' + id, // 接口URL更新广告信息
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含更新后的广告数据
}) })
} }

@ -1,40 +1,110 @@
// 导入封装的 HTTP 请求工具,用于与后端 API 进行通信
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取首页品牌列表
*
* 通过 GET 请求获取首页品牌列表支持分页和条件筛选
*
* @param {Object} params - 查询参数例如分页和筛选条件
* 示例{ pageNum: 1, pageSize: 10, keyword: '品牌名' }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchList({ pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response);
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url:'/home/brand/list', url: '/home/brand/list', // 接口URL获取品牌列表
method:'get', method: 'get', // 请求方法GET
params:params params: params // 查询参数,通过 URL 传递
}) })
} }
/**
* updateRecommendStatus - 批量更新品牌推荐状态
*
* 通过 POST 请求批量更新品牌的推荐状态如是否推荐
*
* @param {Array<Object>} data - 推荐状态数据包含品牌ID数组和推荐状态
* 示例{ ids: [1, 2, 3], recommendStatus: 1 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateRecommendStatus({ ids: [1, 2], recommendStatus: 1 }).then(response => {
* console.log(response);
* });
*/
export function updateRecommendStatus(data) { export function updateRecommendStatus(data) {
return request({ return request({
url:'/home/brand/update/recommendStatus', url: '/home/brand/update/recommendStatus', // 接口URL更新推荐状态
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体包含品牌ID和状态
}) })
} }
/**
* deleteHomeBrand - 批量删除首页品牌
*
* 通过 POST 请求删除多个品牌记录
*
* @param {Array<number|string>} data - 品牌ID数组例如 [1, 2, 3]
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* deleteHomeBrand([1, 2, 3]).then(response => {
* console.log(response);
* });
*/
export function deleteHomeBrand(data) { export function deleteHomeBrand(data) {
return request({ return request({
url:'/home/brand/delete', url: '/home/brand/delete', // 接口URL批量删除品牌
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体包含品牌ID数组
}) })
} }
/**
* createHomeBrand - 创建新的首页品牌
*
* 通过 POST 请求提交新的品牌数据创建首页品牌
*
* @param {Object} data - 品牌的详细数据例如 { brandId: 1, brandName: '品牌名', sort: 0 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* createHomeBrand({ brandId: 1, brandName: '新品牌', sort: 1 }).then(response => {
* console.log(response);
* });
*/
export function createHomeBrand(data) { export function createHomeBrand(data) {
return request({ return request({
url:'/home/brand/create', url: '/home/brand/create', // 接口URL创建品牌
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含品牌数据
}) })
} }
/**
* updateHomeBrandSort - 更新品牌的排序值
*
* 通过 POST 请求更新指定品牌的排序值
*
* @param {Object} params - 排序参数包含品牌ID和新的排序值
* 示例{ id: 1, sort: 10 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateHomeBrandSort({ id: 1, sort: 10 }).then(response => {
* console.log(response);
* });
*/
export function updateHomeBrandSort(params) { export function updateHomeBrandSort(params) {
return request({ return request({
url:'/home/brand/update/sort/'+params.id, url: '/home/brand/update/sort/' + params.id, // 接口URL更新品牌排序
method:'post', method: 'post', // 请求方法POST
params:params params: params // 排序参数,通过 URL 查询字符串传递
}) })
} }

@ -1,40 +1,110 @@
// 导入封装的 HTTP 请求工具,用于与后端 API 进行通信
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取首页推荐专题列表
*
* 通过 GET 请求获取首页推荐专题列表支持分页和条件筛选
*
* @param {Object} params - 查询参数例如分页和筛选条件
* 示例{ pageNum: 1, pageSize: 10, keyword: '专题名' }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchList({ pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response);
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url:'/home/recommendSubject/list', url: '/home/recommendSubject/list', // 接口URL获取推荐专题列表
method:'get', method: 'get', // 请求方法GET
params:params params: params // 查询参数,通过 URL 传递
}) })
} }
/**
* updateRecommendStatus - 批量更新推荐专题的推荐状态
*
* 通过 POST 请求批量更新首页推荐专题的推荐状态
*
* @param {Object} data - 推荐状态数据包含专题ID数组和推荐状态
* 示例{ ids: [1, 2, 3], recommendStatus: 1 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateRecommendStatus({ ids: [1, 2, 3], recommendStatus: 1 }).then(response => {
* console.log(response);
* });
*/
export function updateRecommendStatus(data) { export function updateRecommendStatus(data) {
return request({ return request({
url:'/home/recommendSubject/update/recommendStatus', url: '/home/recommendSubject/update/recommendStatus', // 接口URL更新推荐状态
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体包含专题ID和推荐状态
}) })
} }
/**
* deleteHomeSubject - 批量删除推荐专题
*
* 通过 POST 请求删除多个推荐专题
*
* @param {Array<number|string>} data - 专题ID数组例如 [1, 2, 3]
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* deleteHomeSubject([1, 2, 3]).then(response => {
* console.log(response);
* });
*/
export function deleteHomeSubject(data) { export function deleteHomeSubject(data) {
return request({ return request({
url:'/home/recommendSubject/delete', url: '/home/recommendSubject/delete', // 接口URL批量删除专题
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体包含专题ID数组
}) })
} }
/**
* createHomeSubject - 创建新的首页推荐专题
*
* 通过 POST 请求提交新的专题数据创建首页推荐专题
*
* @param {Object} data - 推荐专题的详细数据例如 { subjectId: 1, subjectName: '专题名', sort: 0 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* createHomeSubject({ subjectId: 1, subjectName: '新专题', sort: 1 }).then(response => {
* console.log(response);
* });
*/
export function createHomeSubject(data) { export function createHomeSubject(data) {
return request({ return request({
url:'/home/recommendSubject/create', url: '/home/recommendSubject/create', // 接口URL创建推荐专题
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含推荐专题数据
}) })
} }
/**
* updateHomeSubjectSort - 更新推荐专题的排序值
*
* 通过 POST 请求更新指定推荐专题的排序值
*
* @param {Object} params - 排序参数包含专题ID和新的排序值
* 示例{ id: 1, sort: 10 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateHomeSubjectSort({ id: 1, sort: 10 }).then(response => {
* console.log(response);
* });
*/
export function updateHomeSubjectSort(params) { export function updateHomeSubjectSort(params) {
return request({ return request({
url:'/home/recommendSubject/update/sort/'+params.id, url: '/home/recommendSubject/update/sort/' + params.id, // 接口URL更新专题排序
method:'post', method: 'post', // 请求方法POST
params:params params: params // 排序参数,通过 URL 查询字符串传递
}) })
} }

@ -1,40 +1,110 @@
// 导入封装的 HTTP 请求工具,用于与后端 API 进行通信
import request from '@/utils/request' import request from '@/utils/request'
/**
* fetchList - 获取首页推荐商品列表
*
* 通过 GET 请求获取首页推荐商品列表支持分页和条件筛选
*
* @param {Object} params - 查询参数例如分页和筛选条件
* 示例{ pageNum: 1, pageSize: 10, keyword: '商品名' }
* @returns {Promise} - 返回一个 Promise 对象包含接口返回的数据
*
* 用法示例
* fetchList({ pageNum: 1, pageSize: 10 }).then(response => {
* console.log(response);
* });
*/
export function fetchList(params) { export function fetchList(params) {
return request({ return request({
url:'/home/recommendProduct/list', url: '/home/recommendProduct/list', // 接口URL获取推荐商品列表
method:'get', method: 'get', // 请求方法GET
params:params params: params // 查询参数,通过 URL 传递
}) })
} }
/**
* updateRecommendStatus - 批量更新推荐商品的推荐状态
*
* 通过 POST 请求批量更新首页推荐商品的推荐状态
*
* @param {Object} data - 推荐状态数据包含商品ID数组和推荐状态
* 示例{ ids: [1, 2, 3], recommendStatus: 1 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateRecommendStatus({ ids: [1, 2, 3], recommendStatus: 1 }).then(response => {
* console.log(response);
* });
*/
export function updateRecommendStatus(data) { export function updateRecommendStatus(data) {
return request({ return request({
url:'/home/recommendProduct/update/recommendStatus', url: '/home/recommendProduct/update/recommendStatus', // 接口URL更新推荐状态
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体包含商品ID和推荐状态
}) })
} }
/**
* deleteHotProduct - 批量删除推荐商品
*
* 通过 POST 请求删除多个推荐商品
*
* @param {Array<number|string>} data - 商品ID数组例如 [1, 2, 3]
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* deleteHotProduct([1, 2, 3]).then(response => {
* console.log(response);
* });
*/
export function deleteHotProduct(data) { export function deleteHotProduct(data) {
return request({ return request({
url:'/home/recommendProduct/delete', url: '/home/recommendProduct/delete', // 接口URL批量删除推荐商品
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体包含商品ID数组
}) })
} }
/**
* createHotProduct - 创建新的首页推荐商品
*
* 通过 POST 请求提交新的商品数据创建首页推荐商品
*
* @param {Object} data - 商品的详细数据例如 { productId: 1, productName: '商品名', sort: 0 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* createHotProduct({ productId: 1, productName: '新商品', sort: 1 }).then(response => {
* console.log(response);
* });
*/
export function createHotProduct(data) { export function createHotProduct(data) {
return request({ return request({
url:'/home/recommendProduct/create', url: '/home/recommendProduct/create', // 接口URL创建推荐商品
method:'post', method: 'post', // 请求方法POST
data:data data: data // 请求体,包含商品数据
}) })
} }
/**
* updateHotProductSort - 更新推荐商品的排序值
*
* 通过 POST 请求更新指定推荐商品的排序值
*
* @param {Object} params - 排序参数包含商品ID和新的排序值
* 示例{ id: 1, sort: 10 }
* @returns {Promise} - 返回一个 Promise 对象包含操作结果
*
* 用法示例
* updateHotProductSort({ id: 1, sort: 10 }).then(response => {
* console.log(response);
* });
*/
export function updateHotProductSort(params) { export function updateHotProductSort(params) {
return request({ return request({
url:'/home/recommendProduct/update/sort/'+params.id, url: '/home/recommendProduct/update/sort/' + params.id, // 接口URL更新商品排序
method:'post', method: 'post', // 请求方法POST
params:params params: params // 排序参数,通过 URL 查询字符串传递
}) })
} }

@ -1,8 +1,13 @@
<template> <template>
<!-- el-breadcrumb 组件用于创建面包屑导航这里给它添加了类名 "app-breadcrumb"并设置分隔符为 "/"用于分隔面包屑中的各个节点 -->
<el-breadcrumb class="app-breadcrumb" separator="/"> <el-breadcrumb class="app-breadcrumb" separator="/">
<!-- transition-group 组件用于对一组元素进行过渡动画效果的包裹这里命名为 "breadcrumb"它会根据内部元素的添加删除移动等操作自动应用过渡动画常用于展示动态变化的元素列表此处用于面包屑导航项的过渡效果处理 -->
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<!-- 使用 v-for 指令遍历 levelList 数组创建多个 el-breadcrumb-item 组件面包屑导航的每一项并将数组中的每个元素item以及对应的索引index传递到循环体内部使用同时通过 :key 绑定每个项的唯一标识这里使用 item.path确保 Vue 能够正确识别每个元素的变化 -->
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path" v-if="item.meta.title"> <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path" v-if="item.meta.title">
<!-- 根据条件判断来决定面包屑导航项的显示方式如果该项的 redirect 属性等于 "noredirect" 或者当前项是 levelList 数组中的最后一项index == levelList.length - 1则使用 span 标签显示该项的标题item.meta.title并且添加类名 "no-redirect"用于后续样式设置 -->
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{item.meta.title}}</span> <span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{item.meta.title}}</span>
<!-- 如果不满足上述条件即可以进行路由跳转的情况则使用 router-link 组件创建一个路由链接通过 :to 属性绑定要跳转的目标路径优先使用 item.redirect如果不存在则使用 item.path并在链接中显示该项的标题item.meta.title点击该链接可导航到对应的路由页面 -->
<router-link v-else :to="item.redirect||item.path">{{item.meta.title}}</router-link> <router-link v-else :to="item.redirect||item.path">{{item.meta.title}}</router-link>
</el-breadcrumb-item> </el-breadcrumb-item>
</transition-group> </transition-group>
@ -11,14 +16,17 @@
<script> <script>
export default { export default {
// created this.getBreadcrumb
created() { created() {
this.getBreadcrumb() this.getBreadcrumb()
}, },
data() { data() {
return { return {
// levelList null getBreadcrumb
levelList: null levelList: null
} }
}, },
// watch $routeVue this.getBreadcrumb
watch: { watch: {
$route() { $route() {
this.getBreadcrumb() this.getBreadcrumb()
@ -26,11 +34,15 @@ export default {
}, },
methods: { methods: {
getBreadcrumb() { getBreadcrumb() {
// this.$route.matched 使 filter name name 便 matched
let matched = this.$route.matched.filter(item => item.name) let matched = this.$route.matched.filter(item => item.name)
const first = matched[0] const first = matched[0]
if (first && first.name !== 'home') { // name 'home'
if (first && first.name!== 'home') {
// matched '/home'meta ''
matched = [{ path: '/home', meta: { title: '首页' }}].concat(matched) matched = [{ path: '/home', meta: { title: '首页' }}].concat(matched)
} }
// matched levelList 使
this.levelList = matched this.levelList = matched
} }
} }
@ -38,14 +50,22 @@ export default {
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
.app-breadcrumb.el-breadcrumb { // "app-breadcrumb" "el-breadcrumb"
display: inline-block; .app-breadcrumb.el-breadcrumb {
font-size: 14px; // 使
line-height: 50px; display: inline-block;
margin-left: 10px; // 14px
.no-redirect { font-size: 14px;
color: #97a8be; // 50px使
cursor: text; line-height: 50px;
} // 10px使
margin-left: 10px;
// "no-redirect"
.no-redirect {
// #97a8be
color: #97a8be;
// cursor: text
cursor: text;
} }
}
</style> </style>

@ -1,116 +1,277 @@
// tinymce.addI18n 方法用于向 TinyMCE 编辑器添加国际化i18n语言包这里添加的是中文简体'zh_CN')语言包。
// 其参数是一个对象,对象的属性名对应着 TinyMCE 编辑器中各种功能或提示文本的标识符,属性值则是对应的中文翻译内容。
tinymce.addI18n('zh_CN',{ tinymce.addI18n('zh_CN',{
"Cut": "\u526a\u5207", // "Cut" 对应的中文翻译为 "剪切",在编辑器中涉及剪切操作相关的功能显示文本时会使用这个翻译。
"Heading 5": "\u6807\u98985", "Cut": "\u526a\u5207",
"Header 2": "\u6807\u98982", // "Heading 5" 翻译为 "标题 5",可能用于表示文档中不同级别的标题,这里是第 5 级标题相关功能展示时对应的文本。
"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u5bf9\u526a\u8d34\u677f\u7684\u8bbf\u95ee\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u952e\u8fdb\u884c\u590d\u5236\u7c98\u8d34\u3002", "Heading 5": "\u6807\u98985",
"Heading 4": "\u6807\u98984", // "Header 2" 翻译为 "标题 2",同样与文档标题级别相关,此处是第 2 级标题对应的文本,可能用于菜单、按钮等地方显示。
"Div": "Div\u533a\u5757", "Header 2": "\u6807\u98982",
"Heading 2": "\u6807\u98982", // 这条较长的文本是一个提示信息,当用户浏览器不支持直接访问剪贴板时显示,提示用户使用 Ctrl + X/C/V 键盘快捷键来进行复制、粘贴、剪切操作。
"Paste": "\u7c98\u8d34", "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u5bf9\u526a\u8d34\u677f\u7684\u8bbf\u95ee\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u952e\u8fdb\u884c\u590d\u5236\u7c98\u8d34\u3002",
"Close": "\u5173\u95ed", "Heading 4": "\u6807\u98984",
"Font Family": "\u5b57\u4f53", // "Div" 翻译为 "Div 区块",可能与 HTML 中的 <div> 标签相关的功能显示文本对应,比如在插入、编辑相关区块操作时使用。
"Pre": "\u9884\u683c\u5f0f\u6587\u672c", "Div": "Div\u533a\u5757",
"Align right": "\u53f3\u5bf9\u9f50", // 重复出现的 "Heading 2",同样表示 "标题 2",与前面功能类似,在不同地方用于显示第 2 级标题相关的文本。
"New document": "\u65b0\u6587\u6863", "Heading 2": "\u6807\u98982",
"Blockquote": "\u5f15\u7528", // "Paste" 翻译为 "粘贴",对应粘贴操作相关功能在界面上显示的文本内容。
"Numbered list": "\u7f16\u53f7\u5217\u8868", "Paste": "\u7c98\u8d34",
"Heading 1": "\u6807\u98981", // "Close" 翻译为 "关闭",常用于关闭对话框、菜单等操作对应的提示文本。
"Headings": "\u6807\u9898", "Close": "\u5173\u95ed",
"Increase indent": "\u589e\u52a0\u7f29\u8fdb", // "Font Family" 翻译为 "字体",在涉及选择字体相关的功能菜单、下拉框等地方会显示该文本,用于提示用户操作的内容。
"Formats": "\u683c\u5f0f", "Font Family": "\u5b57\u4f53",
"Headers": "\u6807\u9898", // "Pre" 翻译为 "预格式化文本",可能与显示代码块、预格式化的文本段落等相关功能的文本显示对应。
"Select all": "\u5168\u9009", "Pre": "\u9884\u683c\u5f0f\u6587\u672c",
"Header 3": "\u6807\u98983", // "Align right" 翻译为 "右对齐",用于在设置文本、图片等元素对齐方式为靠右对齐时显示的文本提示。
"Blocks": "\u533a\u5757", "Align right": "\u53f3\u5bf9\u9f50",
"Undo": "\u64a4\u6d88", // "New document" 翻译为 "新文档",比如在创建新的空白文档等相关操作时会显示该文本作为提示。
"Strikethrough": "\u5220\u9664\u7ebf", "New document": "\u65b0\u6587\u6863",
"Bullet list": "\u9879\u76ee\u7b26\u53f7", // "Blockquote" 翻译为 "引用",可能用于在插入、编辑引用块相关功能显示的文本内容,如引用他人文字等场景。
"Header 1": "\u6807\u98981", "Blockquote": "\u5f15\u7528",
"Superscript": "\u4e0a\u6807", // "Numbered list" 翻译为 "编号列表",对应创建、编辑有序列表相关操作在界面上显示的文本提示。
"Clear formatting": "\u6e05\u9664\u683c\u5f0f", "Numbered list": "\u7f16\u53f7\u5217\u8868",
"Font Sizes": "\u5b57\u53f7", // "Heading 1" 翻译为 "标题 1",表示文档中最高级别的标题相关功能对应的文本,比如文章的主标题相关操作显示文本。
"Subscript": "\u4e0b\u6807", "Heading 1": "\u6807\u98981",
"Header 6": "\u6807\u98986", // "Headings" 翻译为 "标题",是一个比较笼统表示各种标题相关功能的文本,比如标题样式选择等场景会用到。
"Redo": "\u91cd\u590d", "Headings": "\u6807\u9898",
"Paragraph": "\u6bb5\u843d", // "Increase indent" 翻译为 "增加缩进",用于在调整文本段落缩进量,使其更缩进时显示的操作提示文本。
"Ok": "\u786e\u5b9a", "Increase indent": "\u589e\u52a0\u7f29\u8fdb",
"Bold": "\u7c97\u4f53", // "Formats" 翻译为 "格式",常用于格式选择、格式转换等相关功能菜单、按钮等地方显示的文本提示。
"Code": "\u4ee3\u7801", "Formats": "\u683c\u5f0f",
"Italic": "\u659c\u4f53", // "Headers" 翻译为 "标题",与前面类似,也是和文档中各级标题相关功能对应的文本内容。
"Align center": "\u5c45\u4e2d", "Headers": "\u6807\u9898",
"Header 5": "\u6807\u98985", // "Select all" 翻译为 "全选",在提供选择全部内容功能的地方会显示该文本,方便用户操作。
"Heading 6": "\u6807\u98986", "Select all": "\u5168\u9009",
"Heading 3": "\u6807\u98983", // "Header 3" 翻译为 "标题 3",对应文档中第 3 级标题相关功能显示的文本内容。
"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb", "Header 3": "\u6807\u98983",
"Header 4": "\u6807\u98984", // "Blocks" 翻译为 "区块",可能涉及页面中各种块状元素(如不同类型的容器、分区等)相关功能显示的文本。
"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002", "Blocks": "\u533a\u5757",
"Underline": "\u4e0b\u5212\u7ebf", // "Undo" 翻译为 "撤销",用于撤销上一步操作的功能按钮等地方显示的文本提示。
"Cancel": "\u53d6\u6d88", "Undo": "\u64a4\u6d88",
"Justify": "\u4e24\u7aef\u5bf9\u9f50", // "Strikethrough" 翻译为 "删除线",在给文本添加或取消删除线相关操作时显示的文本提示。
"Inline": "\u6587\u672c", "Strikethrough": "\u5220\u9664\u7ebf",
"Copy": "\u590d\u5236", // "Bullet list" 翻译为 "项目符号列表",对应创建、编辑无序列表相关操作在界面上显示的文本提示。
"Align left": "\u5de6\u5bf9\u9f50", "Bullet list": "\u9879\u76ee\u7b26\u53f7",
"Visual aids": "\u7f51\u683c\u7ebf", // 重复的 "Header 1",表示 "标题 1",对应相关标题级别功能显示文本。
"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd", "Header 1": "\u6807\u98981",
"Square": "\u65b9\u5757", // "Superscript" 翻译为 "上标",用于在设置文本为上标形式(如数学公式中的指数等场景)相关操作显示的文本提示。
"Default": "\u9ed8\u8ba4", "Superscript": "\u4e0a\u6807",
"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd", // "Clear formatting" 翻译为 "清除格式",在去除文本已有的格式,恢复到默认格式等相关操作时显示的文本提示。
"Circle": "\u7a7a\u5fc3\u5706", "Clear formatting": "\u6e05\u9664\u683c\u5f0f",
"Disc": "\u5b9e\u5fc3\u5706", // "Font Sizes" 翻译为 "字号",在选择字体大小相关的功能菜单、下拉框等地方会显示该文本,提示用户操作内容。
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd", "Font Sizes": "\u5b57\u53f7",
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd", // "Subscript" 翻译为 "下标",用于在设置文本为下标形式(如化学公式中的下标等场景)相关操作显示的文本提示。
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd", "Subscript": "\u4e0b\u6807",
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002", // "Header 6" 翻译为 "标题 6",对应文档中第 6 级标题相关功能显示的文本内容。
"Name": "\u540d\u79f0", "Header 6": "\u6807\u98986",
"Anchor": "\u951a\u70b9", // "Redo" 翻译为 "重做",用于重新执行上一步被撤销的操作的功能按钮等地方显示的文本提示。
"Id": "\u6807\u8bc6\u7b26", "Redo": "\u91cd\u590d",
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f", // "Paragraph" 翻译为 "段落",与文本段落相关的功能(如段落格式设置、新建段落等)显示的文本提示会用到。
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f", "Paragraph": "\u6bb5\u843d",
"Special character": "\u7279\u6b8a\u7b26\u53f7", // "Ok" 翻译为 "确定",常用于确认操作、提交设置等对话框中的按钮文本提示。
"Source code": "\u6e90\u4ee3\u7801", "Ok": "\u786e\u5b9a",
"Language": "\u8bed\u8a00", // "Bold" 翻译为 "粗体",在将文本设置为加粗字体相关操作时显示的文本提示。
"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b", "Bold": "\u7c97\u4f53",
"B": "B", // "Code" 翻译为 "代码",可能涉及插入、编辑代码块相关功能显示的文本内容,或者切换到代码编辑模式等场景使用。
"R": "R", "Code": "\u4ee3\u7801",
"G": "G", // "Italic" 翻译为 "斜体",在将文本设置为斜体字体相关操作时显示的文本提示。
"Color": "\u989c\u8272", "Italic": "\u659c\u4f53",
"Right to left": "\u4ece\u53f3\u5230\u5de6", // "Align center" 翻译为 "居中对齐",用于在设置文本、图片等元素对齐方式为居中对齐时显示的文本提示。
"Left to right": "\u4ece\u5de6\u5230\u53f3", "Align center": "\u5c45\u4e2d",
"Emoticons": "\u8868\u60c5", // 重复的 "Header 5",表示 "标题 5",对应相关标题级别功能显示文本。
"Robots": "\u673a\u5668\u4eba", "Header 5": "\u6807\u98985",
"Document properties": "\u6587\u6863\u5c5e\u6027", // "Heading 6" 翻译为 "标题 6",对应文档中第 6 级标题相关功能显示的文本内容。
"Title": "\u6807\u9898", "Heading 6": "\u6807\u98986",
"Keywords": "\u5173\u952e\u8bcd", // "Heading 3" 翻译为 "标题 3",对应文档中第 3 级标题相关功能显示的文本内容。
"Encoding": "\u7f16\u7801", "Heading 3": "\u6807\u98983",
"Description": "\u63cf\u8ff0", // "Decrease indent" 翻译为 "减少缩进",用于在调整文本段落缩进量,使其减少缩进时显示的操作提示文本。
"Author": "\u4f5c\u8005", "Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
"Fullscreen": "\u5168\u5c4f", // 重复的 "Header 4",表示 "标题 4",对应相关标题级别功能显示文本。
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf", "Header 4": "\u6807\u98984",
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd", // 这条长文本是一个关于粘贴模式的提示信息,告知用户当前处于纯文本粘贴模式,再次点击相关选项可切换回普通粘贴模式,下次粘贴内容将以纯文本形式粘贴。
"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247", "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u5185\u5bb9\u5c06\u73b0\u5728\u4f1a\u7c98\u8d34\u4e3a\u7eaf\u6587\u672c\u6a21\u5f0f\u3002",
"General": "\u666e\u901a", // "Underline" 翻译为 "下划线",在给文本添加或取消下划线相关操作时显示的文本提示。
"Advanced": "\u9ad8\u7ea7", "Underline": "\u4e0b\u5212\u7ebf",
"Source": "\u5730\u5740", // "Cancel" 翻译为 "取消",常用于取消操作、关闭对话框等不执行当前操作并返回的场景对应的按钮文本提示。
"Border": "\u8fb9\u6846", "Cancel": "\u53d6\u6d88",
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4", // "Justify" 翻译为 "两端对齐",用于在设置文本、段落等元素两端对齐方式时显示的文本提示。
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd", "Justify": "\u4e24\u7aef\u5bf9\u9f50",
"Image description": "\u56fe\u7247\u63cf\u8ff0", // "Inline" 翻译为 "行内",可能涉及行内元素相关功能(如行内样式、行内编辑等)显示的文本内容。
"Style": "\u6837\u5f0f", "Inline": "\u6587\u672c",
"Dimensions": "\u5927\u5c0f", // "Copy" 翻译为 "复制",对应复制操作相关功能在界面上显示的文本内容。
"Insert image": "\u63d2\u5165\u56fe\u7247", "Copy": "\u590d\u5236",
"Image": "\u56fe\u7247", // "Align left" 翻译为 "左对齐",用于在设置文本、图片等元素对齐方式为靠左对齐时显示的文本提示。
"Zoom in": "\u653e\u5927", "Align left": "\u5de6\u5bf9\u9f50",
"Contrast": "\u5bf9\u6bd4\u5ea6", // "Visual aids" 翻译为 "视觉辅助",可能与编辑器中提供的一些辅助查看、编辑的可视化功能(如显示网格线、边框等帮助排版的元素)相关文本提示。
"Back": "\u540e\u9000", "Visual aids": "\u7f51\u683c\u7ebf",
"Gamma": "\u4f3d\u9a6c\u503c", // "Lower Greek" 翻译为 "小写希腊字母",可能与插入特殊字符、编号等相关功能涉及到使用小写希腊字母时显示的文本提示。
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c", "Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
"Resize": "\u8c03\u6574\u5927\u5c0f", // "Square" 翻译为 "方块",可能用于表示某种形状相关的功能(比如项目符号形状等)显示的文本提示。
"Sharpen": "\u9510\u5316", "Square": "\u65b9\u5757",
"Zoom out": "\u7f29\u5c0f", // "Default" 翻译为 "默认",常用于表示恢复到默认设置、使用默认选项等相关功能的文本提示。
"Image options": "\u56fe\u7247\u9009\u9879", "Default": "\u9ed8\u8ba4",
"Apply": "\u5e94\u7528", // "Lower Alpha" 翻译为 "小写英文字母",可能与编号、列表等相关功能使用小写英文字母作为序号时显示的文本提示。
"Brightness": "\u4eae\u5ea6", "Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c", // "Circle" 翻译为 "空心圆",同样可能与项目符号、形状相关功能显示的文本提示有关,比如选择项目符号形状为空心圆等场景。
"Circle": "\u7a7a\u5fc3\u5706",
// "Disc" 翻译为 "实心圆",类似上述情况,用于表示实心圆形状相关功能显示的文本提示,比如项目符号形状选择等。
"Disc": "\u5b9e\u5fc3\u5706",
// "Upper Alpha" 翻译为 "大写英文字母",在相关功能使用大写英文字母作为序号(如编号列表等场景)时显示的文本提示。
"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
// "Upper Roman" 翻译为 "大写罗马数字",与使用大写罗马数字作为序号相关功能(如列表编号等场景)显示的文本提示有关。
"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
// "Lower Roman" 翻译为 "小写罗马数字",用于在相关功能使用小写罗马数字作为序号时显示的文本提示,比如列表编号场景。
"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
// 这条长文本是一个关于标识符Id格式要求的提示信息告知用户标识符应以字母开头后面只能跟字母、数字、破折号、点、冒号或下划线用于在输入相关标识内容时给用户提示规范要求。
"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
// "Name" 翻译为 "名称",常用于各种元素(如文件、链接、对象等)命名相关功能显示的文本提示。
"Name": "\u540d\u79f0",
// "Anchor" 翻译为 "锚点",在网页编辑中与设置锚点(用于页面内快速定位等功能)相关操作显示的文本提示。
// "Anchor" 翻译为 "锚点",在网页编辑等场景下,通常用于表示页面内的定位标记(锚点)相关功能对应的文本,
// 比如设置锚点、跳转到锚点位置等操作时,界面上显示的提示文字等会使用这个翻译后的文本。
"Anchor": "\u951a\u70b9",
// "Id" 翻译为 "标识符",常用于标识页面元素、数据记录等具有唯一性的标识符号相关功能显示的文本,
// 例如在 HTML 中元素的 id 属性设置、脚本中通过标识符来获取或操作特定对象等场景会用到该文本提示。
"Id": "\u6807\u8bc6\u7b26",
// 这是一个提示性的文本信息,用于当用户在文档有未保存更改的情况下,尝试离开当前页面(如切换页面、关闭窗口等操作)时弹出提示,
// 询问用户是否确定要离开,提醒用户可能存在数据丢失的风险。
"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
// "Restore last draft" 翻译为 "恢复上次草稿",常用于文档编辑软件中,当用户想要找回上一次保存或自动保存的草稿内容时,
// 点击对应功能按钮等操作时会显示该文本作为提示,告知用户此操作的作用。
"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
// "Special character" 翻译为 "特殊字符",在涉及插入、选择特殊符号(如数学符号、版权符号等非常规字母数字字符)相关功能时,
// 界面上对应的菜单、按钮等地方会显示该文本作为操作提示,引导用户进行相应操作。
"Special character": "\u7279\u6b8a\u7b26\u53f7",
// "Source code" 翻译为 "源代码",多用于代码编辑、网页开发等场景,比如查看或编辑 HTML、JavaScript 等代码时,
// 切换到代码视图或者相关代码编辑区域显示该文本,表明此处展示的是原始的代码内容。
"Source code": "\u6e90\u4ee3\u7801",
// "Language" 翻译为 "语言",通常用于选择文档语言、编程语言、界面语言等相关功能的文本提示,
// 例如在国际化设置中选择不同语言版本,或者代码编辑器中切换代码语言模式等场景会出现该文本。
"Language": "\u8bed\u8a00",
// "Insert/Edit code sample" 翻译为 "插入/编辑代码示例",用于在文档编辑、代码展示等场景下,
// 当用户想要添加一段代码示例或者对已有的代码示例进行修改编辑时,对应操作按钮、菜单选项等地方会显示该文本作为提示。
"Insert/Edit code sample": "\u63d2\u5165/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
// "B"、"R"、"G" 这几个单独的字母可能分别代表颜色中的蓝色Blue、红色Red、绿色Green分量相关含义
// 在涉及颜色设置(如 RGB 颜色模式下调整各颜色分量数值等场景)时会作为相关标识使用,具体使用场景需结合具体代码逻辑判断。
"B": "B",
"R": "R",
"G": "G",
// "Color" 翻译为 "颜色",是一个比较通用的表示与颜色相关功能的文本,比如选择字体颜色、背景颜色、元素颜色等操作时,
// 对应的菜单、对话框等地方会显示该文本作为操作提示,引导用户进行颜色相关的设置。
"Color": "\u989c\u8272",
// "Right to left" 翻译为 "从右到左",常用于文本排版、界面布局等涉及方向设置的功能场景,
// 例如一些支持从右向左书写语言(如阿拉伯语等)的排版,或者切换元素排列方向等操作时会显示该文本提示。
"Right to left": "\u4ece\u53f3\u5230\u5de6",
// "Left to right" 翻译为 "从左到右",与上面对应,是常规的文本、元素排列方向设置相关功能显示的文本,
// 比如设置文本阅读顺序、界面元素排列顺序等操作时会出现该文本作为提示信息。
"Left to right": "\u4ece\u5de6\u5230\u53f3",
// "Emoticons" 翻译为 "表情符号",在支持插入表情符号来丰富文档内容的编辑器中,
// 当用户点击插入表情相关功能按钮、打开表情选择菜单等操作时会显示该文本作为提示,引导用户选择表情符号。
"Emoticons": "\u8868\u60c5",
// "Robots" 翻译为 "机器人",具体含义要根据具体应用场景判断,可能涉及到网页爬虫相关设置(搜索引擎机器人等)、
// 自动化脚本模拟机器人操作等相关功能显示该文本作为相关提示内容。
"Robots": "\u673a\u5668\u4eba",
// "Document properties" 翻译为 "文档属性",常用于查看或编辑文档的各种元数据信息,如文档标题、作者、创建时间、
// 关键词等相关功能的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看或修改这些属性。
"Document properties": "\u6587\u6863\u5c5e\u6027",
// "Title" 翻译为 "标题",用途广泛,可以表示文档的主标题、页面标题、章节标题等相关功能显示的文本,
// 例如在设置文档标题栏显示内容、文章各级标题编辑等场景都会用到该文本提示。
"Title": "\u6807\u9898",
// "Keywords" 翻译为 "关键词",在文档属性设置、搜索引擎优化等场景下,用于填写、编辑能代表文档核心内容的关键词语,
// 相关功能区域(如文档元数据编辑框等)会显示该文本作为操作提示,引导用户输入关键词。
"Keywords": "\u5173\u952e\u8bcd",
// "Encoding" 翻译为 "编码",常用于设置文档、网页等的字符编码格式(如 UTF-8、GBK 等)相关功能,
// 在选择编码类型、查看当前编码等操作对应的菜单、对话框等地方会显示该文本作为提示信息。
"Encoding": "\u7f16\u7801",
// "Description" 翻译为 "描述",常用来表示对文档、网页等内容的简短说明文字,
// 例如在文档元数据中填写文档简介、网页的描述信息用于搜索引擎展示摘要等场景会用到该文本提示用户输入相关内容。
"Description": "\u63cf\u8ff0",
// "Author" 翻译为 "作者",用于在文档属性等相关功能中填写、显示文档创作者的信息,
// 对应功能区域(如文档作者编辑框等)会显示该文本作为操作提示,引导用户输入或查看作者名称。
"Author": "\u4f5c\u8005",
// "Fullscreen" 翻译为 "全屏",在应用程序、网页等提供全屏显示功能的地方,
// 当用户点击全屏按钮、切换到全屏模式等操作时会显示该文本作为提示,告知用户当前进入了全屏状态或可进入全屏状态。
"Fullscreen": "\u5168\u5c4f",
// "Horizontal line" 翻译为 "水平线",在文档编辑、页面设计等场景下,用于插入、编辑水平分割线相关操作时,
// 对应的菜单、按钮等地方会显示该文本作为操作提示,引导用户进行插入或修改水平线的操作。
"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
// "Horizontal space" 翻译为 "水平间距",常用于调整页面元素、文本段落等在水平方向上的间隔距离相关功能,
// 例如在排版设置中修改元素之间的水平空白大小等操作时会显示该文本作为操作提示。
"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
// "Insert/edit image" 翻译为 "插入/编辑图片",在文档编辑、网页内容编辑等场景下,
// 当用户想要添加一张新图片或者对已有的图片进行修改编辑(如调整大小、更换图片等操作)时,
// 对应的操作按钮、菜单选项等地方会显示该文本作为提示,引导用户进行相应操作。
"Insert/edit image": "\u63d2\u5165/\u7f16\u8f91\u56fe\u7247",
// "General" 翻译为 "常规",常用于表示一组设置选项中的通用、基础设置部分,
// 例如在软件的设置对话框中,会将一些常见的、基本的设置分类到 "常规" 选项卡下,对应的选项卡标题会显示该文本作为提示。
"General": "\u666e\u901a",
// "Advanced" 翻译为 "高级",与 "常规" 相对,通常用于存放更复杂、专业、进阶的设置选项,
// 只有熟悉相关功能或者有特定需求的用户才会去调整这些设置,在设置界面中对应的选项卡标题等地方会显示该文本作为提示。
"Advanced": "\u9ad8\u7ea7",
// "Source" 翻译为 "来源",在很多场景下有不同含义,比如图片、视频等媒体资源的原始地址(来源链接),
// 或者代码片段的引用来源等相关功能显示该文本作为提示信息,引导用户填写或查看相应的来源信息。
"Source": "\u5730\u5740",
// "Border" 翻译为 "边框",常用于设置页面元素(如图片、表格、文本框等)的边框样式、宽度、颜色等相关功能,
// 对应的边框设置菜单、对话框等地方会显示该文本作为操作提示,引导用户进行边框相关的设置。
"Border": "\u8fb9\u6846",
// "Constrain proportions" 翻译为 "保持比例",在调整图片、图形等元素大小时,
// 如果希望元素的宽高比例保持不变(避免拉伸变形),相关操作按钮、设置选项等地方会显示该文本作为提示,
// 告知用户当前设置可保持元素原有比例进行缩放操作。
"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
// "Vertical space" 翻译为 "垂直间距",类似于 "Horizontal space",不过是用于调整页面元素、文本段落等在垂直方向上的间隔距离相关功能,
// 比如在排版中设置行间距、元素上下间隔等操作时会显示该文本作为操作提示。
"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
// "Image description" 翻译为 "图片描述",常用于为图片添加说明性文字,
// 比如在网页中方便搜索引擎识别图片内容、辅助视障人士理解图片含义等场景下,会有填写图片描述的功能,对应功能区域会显示该文本提示。
"Image description": "\u56fe\u7247\u63cf\u8ff0",
// "Style" 翻译为 "样式",在文档编辑、网页开发等场景下应用广泛,用于设置文本、元素的各种外观样式(如字体样式、颜色样式、布局样式等),
// 对应的样式设置菜单、选项框等地方会显示该文本作为操作提示,引导用户进行样式相关的选择和设置。
"Style": "\u6837\u5f0f",
// "Dimensions" 翻译为 "尺寸",常用于查看或修改图片、表格、页面区域等元素的大小(宽度和高度)相关功能,
// 例如在调整元素大小时,对应的尺寸输入框、调整按钮等地方会显示该文本作为操作提示,引导用户操作。
"Dimensions": "\u5927\u5c0f",
// "Insert image" 翻译为 "插入图片",是一个比较简洁明确的提示文本,用于在文档、网页等内容中添加新图片时,
// 对应的操作按钮、菜单选项等地方会显示该文本,引导用户进行插入图片的操作。
"Insert image": "\u63d2\u5165\u56fe\u7247",
// "Image" 翻译为 "图片",是一个通用表示图片相关功能的文本,比如在图片管理、选择图片资源等场景下,
// 对应的菜单、列表等地方会显示该文本作为提示,让用户明确当前操作涉及的对象是图片。
"Image": "\u56fe\u7247",
// "Zoom in" 翻译为 "放大",常用于图片查看、文档页面查看等场景下,当用户想要将显示内容放大以便查看细节时,
// 对应的操作按钮(如放大镜加 + 图标按钮等)会显示该文本作为操作提示,引导用户进行放大操作。
"Zoom in": "\u653e\u5927",
// "Contrast" 翻译为 "对比度",在图片编辑、显示设置等场景下,用于调整图片或屏幕显示内容的对比度相关操作时,
// 对应的对比度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行对比度的调整。
"Contrast": "\u5bf9\u6bd4\u5ea6",
// "Back" 翻译为 "后退",常用于页面导航、操作历史回退等场景,比如在浏览器中返回上一页、
// 软件中撤销上一步操作回到之前的状态等操作时,对应的按钮等地方会显示该文本作为操作提示。
"Back": "\u540e\u9000",
// "Gamma" 翻译为 "伽马值",在涉及图像、显示屏等颜色校正、色彩调整相关功能中,伽马值是一个重要参数,
// 当用户调整伽马值来改变显示效果时,对应的设置区域会显示该文本作为操作提示,引导用户进行操作。
"Gamma": "\u4f3d\u9a6c\u503c",
// "Flip horizontally" 翻译为 "水平翻转",在图片编辑、图形处理等场景下,当用户想要将元素在水平方向上进行镜像翻转时,
// 对应的操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行水平翻转操作。
"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
// "Resize" 翻译为 "调整大小",常用于图片、表格、窗口等元素改变尺寸大小相关操作的提示文本,
// 对应的大小调整手柄、输入尺寸数值的框等地方会显示该文本作为操作提示,引导用户进行大小调整操作。
"Resize": "\u8c03\u6574\u5927\u5c0f",
// "Sharpen" 翻译为 "锐化",在图片编辑中,用于提高图片清晰度、使图像边缘更锐利等相关操作时,
// 对应的锐化操作按钮、调整参数滑块等地方会显示该文本作为操作提示,引导用户进行锐化操作。
"Sharpen": "\u9510\u5316",
// "Zoom out" 翻译为 "缩小",与 "Zoom in" 相对,常用于图片查看、文档页面查看等场景下,当用户想要将显示内容缩小以便查看整体情况时,
// 对应的操作按钮(如放大镜加 - 图标按钮等)会显示该文本作为操作提示,引导用户进行缩小操作。
"Zoom out": "\u7f29\u5c0f",
// "Image options" 翻译为 "图片选项",在图片相关功能中,用于展开更多关于图片的设置(如样式、格式、链接等各种设置)时,
// 对应的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看和调整图片的各项具体设置。
"Image options": "\u56fe\u7247\u9009\u9879",
// "Apply" 翻译为 "应用",常用于当用户在设置界面调整好各项参数、选项后,点击该按钮来使设置生效,
// 对应的操作按钮上会显示该文本作为提示,告知用户点击此按钮可将当前设置应用到相应对象上。
"Apply": "\u5e94\u7528",
// "Brightness" 翻译为 "亮度",在图片编辑、显示屏亮度调节等场景下,用于调整图片或屏幕显示内容的明亮程度相关操作时,
// 对应的亮度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行亮度的调整。
"Brightness": "\u4eae\u5ea6",
// "Rotate clockwise" 翻译为 "顺时针旋转",在图片、图形等元素进行旋转操作时,用于提示用户按照顺时针方向旋转元素,
// 对应的旋转操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行顺时针旋转操作。
"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c", "Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
"Edit image": "\u7f16\u8f91\u56fe\u7247", "Edit image": "\u7f16\u8f91\u56fe\u7247",
"Color levels": "\u989c\u8272\u5c42\u6b21", "Color levels": "\u989c\u8272\u5c42\u6b21",
@ -145,6 +306,463 @@ tinymce.addI18n('zh_CN',{
"Page break": "\u5206\u9875\u7b26", "Page break": "\u5206\u9875\u7b26",
"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c", "Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
"Preview": "\u9884\u89c8", "Preview": "\u9884\u89c8",
//// "Color" 翻译为 "颜色",是一个比较通用的表示与颜色相关功能的文本,比如选择字体颜色、背景颜色、元素颜色等操作时,
// // 对应的菜单、对话框等地方会显示该文本作为操作提示,引导用户进行颜色相关的设置。
// "Color": "\u989c\u8272",
// // "Right to left" 翻译为 "从右到左",常用于文本排版、界面布局等涉及方向设置的功能场景,
// // 例如一些支持从右向左书写语言(如阿拉伯语等)的排版,或者切换元素排列方向等操作时会显示该文本提示。
// "Right to left": "\u4ece\u53f3\u5230\u5de6",
// // "Left to right" 翻译为 "从左到右",与上面对应,是常规的文本、元素排列方向设置相关功能显示的文本,
// // 比如设置文本阅读顺序、界面元素排列顺序等操作时会出现该文本作为提示信息。
// "Left to right": "\u4ece\u5de6\u5230\u53f3",
// // "Emoticons" 翻译为 "表情符号",在支持插入表情符号来丰富文档内容的编辑器中,
// // 当用户点击插入表情相关功能按钮、打开表情选择菜单等操作时会显示该文本作为提示,引导用户选择表情符号。
// "Emoticons": "\u8868\u60c5",
// // "Robots" 翻译为 "机器人",具体含义要根据具体应用场景判断,可能涉及到网页爬虫相关设置(搜索引擎机器人等)、
// // 自动化脚本模拟机器人操作等相关功能显示该文本作为相关提示内容。
// "Robots": "\u673a\u5668\u4eba",
// // "Document properties" 翻译为 "文档属性",常用于查看或编辑文档的各种元数据信息,如文档标题、作者、创建时间、
// // 关键词等相关功能的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看或修改这些属性。
// "Document properties": "\u6587\u6863\u5c5e\u6027",
// // "Title" 翻译为 "标题",用途广泛,可以表示文档的主标题、页面标题、章节标题等相关功能显示的文本,
// // 例如在设置文档标题栏显示内容、文章各级标题编辑等场景都会用到该文本提示。
// "Title": "\u6807\u9898",
// // "Keywords" 翻译为 "关键词",在文档属性设置、搜索引擎优化等场景下,用于填写、编辑能代表文档核心内容的关键词语,
// // 相关功能区域(如文档元数据编辑框等)会显示该文本作为操作提示,引导用户输入关键词。
// "Keywords": "\u5173\u952e\u8bcd",
// // "Encoding" 翻译为 "编码",常用于设置文档、网页等的字符编码格式(如 UTF-8、GBK 等)相关功能,
// // 在选择编码类型、查看当前编码等操作对应的菜单、对话框等地方会显示该文本作为提示信息。
// "Encoding": "\u7f16\u7801",
// // "Description" 翻译为 "描述",常用来表示对文档、网页等内容的简短说明文字,
// // 例如在文档元数据中填写文档简介、网页的描述信息用于搜索引擎展示摘要等场景会用到该文本提示用户输入相关内容。
// "Description": "\u63cf\u8ff0",
// // "Author" 翻译为 "作者",用于在文档属性等相关功能中填写、显示文档创作者的信息,
// // 对应功能区域(如文档作者编辑框等)会显示该文本作为操作提示,引导用户输入或查看作者名称。
// "Author": "\u4f5c\u8005",
// // "Fullscreen" 翻译为 "全屏",在应用程序、网页等提供全屏显示功能的地方,
// // 当用户点击全屏按钮、切换到全屏模式等操作时会显示该文本作为提示,告知用户当前进入了全屏状态或可进入全屏状态。
// "Fullscreen": "\u5168\u5c4f",
// // "Horizontal line" 翻译为 "水平线",在文档编辑、页面设计等场景下,用于插入、编辑水平分割线相关操作时,
// // 对应的菜单、按钮等地方会显示该文本作为操作提示,引导用户进行插入或修改水平线的操作。
// "Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
// // "Horizontal space" 翻译为 "水平间距",常用于调整页面元素、文本段落等在水平方向上的间隔距离相关功能,
// // 例如在排版设置中修改元素之间的水平空白大小等操作时会显示该文本作为操作提示。
// "Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
// // "Insert/edit image" 翻译为 "插入/编辑图片",在文档编辑、网页内容编辑等场景下,
// // 当用户想要添加一张新图片或者对已有的图片进行修改编辑(如调整大小、更换图片等操作)时,
// // 对应的操作按钮、菜单选项等地方会显示该文本作为提示,引导用户进行相应操作。
// "Insert/edit image": "\u63d2\u5165/\u7f16\u8f91\u56fe\u7247",
// // "General" 翻译为 "常规",常用于表示一组设置选项中的通用、基础设置部分,
// // 例如在软件的设置对话框中,会将一些常见的、基本的设置分类到 "常规" 选项卡下,对应的选项卡标题会显示该文本作为提示。
// "General": "\u666e\u901a",
// // "Advanced" 翻译为 "高级",与 "常规" 相对,通常用于存放更复杂、专业、进阶的设置选项,
// // 只有熟悉相关功能或者有特定需求的用户才会去调整这些设置,在设置界面中对应的选项卡标题等地方会显示该文本作为提示。
// "Advanced": "\u9ad8\u7ea7",
// // "Source" 翻译为 "来源",在很多场景下有不同含义,比如图片、视频等媒体资源的原始地址(来源链接),
// // 或者代码片段的引用来源等相关功能显示该文本作为提示信息,引导用户填写或查看相应的来源信息。
// "Source": "\u5730\u5740",
// // "Border" 翻译为 "边框",常用于设置页面元素(如图片、表格、文本框等)的边框样式、宽度、颜色等相关功能,
// // 对应的边框设置菜单、对话框等地方会显示该文本作为操作提示,引导用户进行边框相关的设置。
// "Border": "\u8fb9\u6846",
// // "Constrain proportions" 翻译为 "保持比例",在调整图片、图形等元素大小时,
// // 如果希望元素的宽高比例保持不变(避免拉伸变形),相关操作按钮、设置选项等地方会显示该文本作为提示,
// // 告知用户当前设置可保持元素原有比例进行缩放操作。
// "Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
// // "Vertical space" 翻译为 "垂直间距",类似于 "Horizontal space",不过是用于调整页面元素、文本段落等在垂直方向上的间隔距离相关功能,
// // 比如在排版中设置行间距、元素上下间隔等操作时会显示该文本作为操作提示。
// "Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
// // "Image description" 翻译为 "图片描述",常用于为图片添加说明性文字,
// // 比如在网页中方便搜索引擎识别图片内容、辅助视障人士理解图片含义等场景下,会有填写图片描述的功能,对应功能区域会显示该文本提示。
// "Image description": "\u56fe\u7247\u63cf\u8ff0",
// // "Style" 翻译为 "样式",在文档编辑、网页开发等场景下应用广泛,用于设置文本、元素的各种外观样式(如字体样式、颜色样式、布局样式等),
// // 对应的样式设置菜单、选项框等地方会显示该文本作为操作提示,引导用户进行样式相关的选择和设置。
// "Style": "\u6837\u5f0f",
// // "Dimensions" 翻译为 "尺寸",常用于查看或修改图片、表格、页面区域等元素的大小(宽度和高度)相关功能,
// // 例如在调整元素大小时,对应的尺寸输入框、调整按钮等地方会显示该文本作为操作提示,引导用户操作。
// "Dimensions": "\u5927\u5c0f",
// // "Insert image" 翻译为 "插入图片",是一个比较简洁明确的提示文本,用于在文档、网页等内容中添加新图片时,
// // 对应的操作按钮、菜单选项等地方会显示该文本,引导用户进行插入图片的操作。
// "Insert image": "\u63d2\u5165\u56fe\u7247",
// // "Image" 翻译为 "图片",是一个通用表示图片相关功能的文本,比如在图片管理、选择图片资源等场景下,
// // 对应的菜单、列表等地方会显示该文本作为提示,让用户明确当前操作涉及的对象是图片。
// "Image": "\u56fe\u7247",
// // "Zoom in" 翻译为 "放大",常用于图片查看、文档页面查看等场景下,当用户想要将显示内容放大以便查看细节时,
// // 对应的操作按钮(如放大镜加 + 图标按钮等)会显示该文本作为操作提示,引导用户进行放大操作。
// "Zoom in": "\u653e\u5927",
// // "Contrast" 翻译为 "对比度",在图片编辑、显示设置等场景下,用于调整图片或屏幕显示内容的对比度相关操作时,
// // 对应的对比度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行对比度的调整。
// "Contrast": "\u5bf9\u6bd4\u5ea6",
// // "Back" 翻译为 "后退",常用于页面导航、操作历史回退等场景,比如在浏览器中返回上一页、
// // 软件中撤销上一步操作回到之前的状态等操作时,对应的按钮等地方会显示该文本作为操作提示。
// "Back": "\u540e\u9000",
// // "Gamma" 翻译为 "伽马值",在涉及图像、显示屏等颜色校正、色彩调整相关功能中,伽马值是一个重要参数,
// // 当用户调整伽马值来改变显示效果时,对应的设置区域会显示该文本作为操作提示,引导用户进行操作。
// "Gamma": "\u4f3d\u9a6c\u503c",
// // "Flip horizontally" 翻译为 "水平翻转",在图片编辑、图形处理等场景下,当用户想要将元素在水平方向上进行镜像翻转时,
// // 对应的操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行水平翻转操作。
// "Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
// // "Resize" 翻译为 "调整大小",常用于图片、表格、窗口等元素改变尺寸大小相关操作的提示文本,
// // 对应的大小调整手柄、输入尺寸数值的框等地方会显示该文本作为操作提示,引导用户进行大小调整操作。
// "Resize": "\u8c03\u6574\u5927\u5c0f",
// // "Sharpen" 翻译为 "锐化",在图片编辑中,用于提高图片清晰度、使图像边缘更锐利等相关操作时,
// // 对应的锐化操作按钮、调整参数滑块等地方会显示该文本作为操作提示,引导用户进行锐化操作。
// "Sharpen": "\u9510\u5316",
// // "Zoom out" 翻译为 "缩小",与 "Zoom in" 相对,常用于图片查看、文档页面查看等场景下,当用户想要将显示内容缩小以便查看整体情况时,
// // 对应的操作按钮(如放大镜加 - 图标按钮等)会显示该文本作为操作提示,引导用户进行缩小操作。
// "Zoom out": "\u7f29\u5c0f",
// // "Image options" 翻译为 "图片选项",在图片相关功能中,用于展开更多关于图片的设置(如样式、格式、链接等各种设置)时,
// // 对应的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看和调整图片的各项具体设置。
// "Image options": "\u56fe\u7247\u9009\u9879",
// // "Apply" 翻译为 "应用",常用于当用户在设置界面调整好各项参数、选项后,点击该按钮来使设置生效,
// // 对应的操作按钮上会显示该文本作为提示,告知用户点击此按钮可将当前设置应用到相应对象上。
// "Apply": "\u5e94\u7528",
// // "Brightness" 翻译为 "亮度",在图片编辑、显示屏亮度调节等场景下,用于调整图片或屏幕显示内容的明亮程度相关操作时,
// // 对应的亮度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行亮度的调整。
// "Brightness": "\u4eae\u5ea6",
// // "Rotate clockwise" 翻译为 "顺时针旋转",在图片、图形等元素进行旋转操作时,用于提示用户按照顺时针方向旋转元素,
// // 对应的旋转操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行顺时针旋转操作。// "Color" 翻译为 "颜色",是一个比较通用的表示与颜色相关功能的文本,比如选择字体颜色、背景颜色、元素颜色等操作时,
// // 对应的菜单、对话框等地方会显示该文本作为操作提示,引导用户进行颜色相关的设置。
// "Color": "\u989c\u8272",
// // "Right to left" 翻译为 "从右到左",常用于文本排版、界面布局等涉及方向设置的功能场景,
// // 例如一些支持从右向左书写语言(如阿拉伯语等)的排版,或者切换元素排列方向等操作时会显示该文本提示。
// "Right to left": "\u4ece\u53f3\u5230\u5de6",
// // "Left to right" 翻译为 "从左到右",与上面对应,是常规的文本、元素排列方向设置相关功能显示的文本,
// // 比如设置文本阅读顺序、界面元素排列顺序等操作时会出现该文本作为提示信息。
// "Left to right": "\u4ece\u5de6\u5230\u53f3",
// // "Emoticons" 翻译为 "表情符号",在支持插入表情符号来丰富文档内容的编辑器中,
// // 当用户点击插入表情相关功能按钮、打开表情选择菜单等操作时会显示该文本作为提示,引导用户选择表情符号。
// "Emoticons": "\u8868\u60c5",
// // "Robots" 翻译为 "机器人",具体含义要根据具体应用场景判断,可能涉及到网页爬虫相关设置(搜索引擎机器人等)、
// // 自动化脚本模拟机器人操作等相关功能显示该文本作为相关提示内容。
// "Robots": "\u673a\u5668\u4eba",
// // "Document properties" 翻译为 "文档属性",常用于查看或编辑文档的各种元数据信息,如文档标题、作者、创建时间、
// // 关键词等相关功能的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看或修改这些属性。
// "Document properties": "\u6587\u6863\u5c5e\u6027",
// // "Title" 翻译为 "标题",用途广泛,可以表示文档的主标题、页面标题、章节标题等相关功能显示的文本,
// // 例如在设置文档标题栏显示内容、文章各级标题编辑等场景都会用到该文本提示。
// "Title": "\u6807\u9898",
// // "Keywords" 翻译为 "关键词",在文档属性设置、搜索引擎优化等场景下,用于填写、编辑能代表文档核心内容的关键词语,
// // 相关功能区域(如文档元数据编辑框等)会显示该文本作为操作提示,引导用户输入关键词。
// "Keywords": "\u5173\u952e\u8bcd",
// // "Encoding" 翻译为 "编码",常用于设置文档、网页等的字符编码格式(如 UTF-8、GBK 等)相关功能,
// // 在选择编码类型、查看当前编码等操作对应的菜单、对话框等地方会显示该文本作为提示信息。
// "Encoding": "\u7f16\u7801",
// // "Description" 翻译为 "描述",常用来表示对文档、网页等内容的简短说明文字,
// // 例如在文档元数据中填写文档简介、网页的描述信息用于搜索引擎展示摘要等场景会用到该文本提示用户输入相关内容。
// "Description": "\u63cf\u8ff0",
// // "Author" 翻译为 "作者",用于在文档属性等相关功能中填写、显示文档创作者的信息,
// // 对应功能区域(如文档作者编辑框等)会显示该文本作为操作提示,引导用户输入或查看作者名称。
// "Author": "\u4f5c\u8005",
// // "Fullscreen" 翻译为 "全屏",在应用程序、网页等提供全屏显示功能的地方,
// // 当用户点击全屏按钮、切换到全屏模式等操作时会显示该文本作为提示,告知用户当前进入了全屏状态或可进入全屏状态。
// "Fullscreen": "\u5168\u5c4f",
// // "Horizontal line" 翻译为 "水平线",在文档编辑、页面设计等场景下,用于插入、编辑水平分割线相关操作时,
// // 对应的菜单、按钮等地方会显示该文本作为操作提示,引导用户进行插入或修改水平线的操作。
// "Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
// // "Horizontal space" 翻译为 "水平间距",常用于调整页面元素、文本段落等在水平方向上的间隔距离相关功能,
// // 例如在排版设置中修改元素之间的水平空白大小等操作时会显示该文本作为操作提示。
// "Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
// // "Insert/edit image" 翻译为 "插入/编辑图片",在文档编辑、网页内容编辑等场景下,
// // 当用户想要添加一张新图片或者对已有的图片进行修改编辑(如调整大小、更换图片等操作)时,
// // 对应的操作按钮、菜单选项等地方会显示该文本作为提示,引导用户进行相应操作。
// "Insert/edit image": "\u63d2\u5165/\u7f16\u8f91\u56fe\u7247",
// // "General" 翻译为 "常规",常用于表示一组设置选项中的通用、基础设置部分,
// // 例如在软件的设置对话框中,会将一些常见的、基本的设置分类到 "常规" 选项卡下,对应的选项卡标题会显示该文本作为提示。
// "General": "\u666e\u901a",
// // "Advanced" 翻译为 "高级",与 "常规" 相对,通常用于存放更复杂、专业、进阶的设置选项,
// // 只有熟悉相关功能或者有特定需求的用户才会去调整这些设置,在设置界面中对应的选项卡标题等地方会显示该文本作为提示。
// "Advanced": "\u9ad8\u7ea7",
// // "Source" 翻译为 "来源",在很多场景下有不同含义,比如图片、视频等媒体资源的原始地址(来源链接),
// // 或者代码片段的引用来源等相关功能显示该文本作为提示信息,引导用户填写或查看相应的来源信息。
// "Source": "\u5730\u5740",
// // "Border" 翻译为 "边框",常用于设置页面元素(如图片、表格、文本框等)的边框样式、宽度、颜色等相关功能,
// // 对应的边框设置菜单、对话框等地方会显示该文本作为操作提示,引导用户进行边框相关的设置。
// "Border": "\u8fb9\u6846",
// // "Constrain proportions" 翻译为 "保持比例",在调整图片、图形等元素大小时,
// // 如果希望元素的宽高比例保持不变(避免拉伸变形),相关操作按钮、设置选项等地方会显示该文本作为提示,
// // 告知用户当前设置可保持元素原有比例进行缩放操作。
// "Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
// // "Vertical space" 翻译为 "垂直间距",类似于 "Horizontal space",不过是用于调整页面元素、文本段落等在垂直方向上的间隔距离相关功能,
// // 比如在排版中设置行间距、元素上下间隔等操作时会显示该文本作为操作提示。
// "Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
// // "Image description" 翻译为 "图片描述",常用于为图片添加说明性文字,
// // 比如在网页中方便搜索引擎识别图片内容、辅助视障人士理解图片含义等场景下,会有填写图片描述的功能,对应功能区域会显示该文本提示。
// "Image description": "\u56fe\u7247\u63cf\u8ff0",
// // "Style" 翻译为 "样式",在文档编辑、网页开发等场景下应用广泛,用于设置文本、元素的各种外观样式(如字体样式、颜色样式、布局样式等),
// // 对应的样式设置菜单、选项框等地方会显示该文本作为操作提示,引导用户进行样式相关的选择和设置。
// "Style": "\u6837\u5f0f",
// // "Dimensions" 翻译为 "尺寸",常用于查看或修改图片、表格、页面区域等元素的大小(宽度和高度)相关功能,
// // 例如在调整元素大小时,对应的尺寸输入框、调整按钮等地方会显示该文本作为操作提示,引导用户操作。
// "Dimensions": "\u5927\u5c0f",
// // "Insert image" 翻译为 "插入图片",是一个比较简洁明确的提示文本,用于在文档、网页等内容中添加新图片时,
// // 对应的操作按钮、菜单选项等地方会显示该文本,引导用户进行插入图片的操作。
// "Insert image": "\u63d2\u5165\u56fe\u7247",
// // "Image" 翻译为 "图片",是一个通用表示图片相关功能的文本,比如在图片管理、选择图片资源等场景下,
// // 对应的菜单、列表等地方会显示该文本作为提示,让用户明确当前操作涉及的对象是图片。
// "Image": "\u56fe\u7247",
// // "Zoom in" 翻译为 "放大",常用于图片查看、文档页面查看等场景下,当用户想要将显示内容放大以便查看细节时,
// // 对应的操作按钮(如放大镜加 + 图标按钮等)会显示该文本作为操作提示,引导用户进行放大操作。
// "Zoom in": "\u653e\u5927",
// // "Contrast" 翻译为 "对比度",在图片编辑、显示设置等场景下,用于调整图片或屏幕显示内容的对比度相关操作时,
// // 对应的对比度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行对比度的调整。
// "Contrast": "\u5bf9\u6bd4\u5ea6",
// // "Back" 翻译为 "后退",常用于页面导航、操作历史回退等场景,比如在浏览器中返回上一页、
// // 软件中撤销上一步操作回到之前的状态等操作时,对应的按钮等地方会显示该文本作为操作提示。
// "Back": "\u540e\u9000",
// // "Gamma" 翻译为 "伽马值",在涉及图像、显示屏等颜色校正、色彩调整相关功能中,伽马值是一个重要参数,
// // 当用户调整伽马值来改变显示效果时,对应的设置区域会显示该文本作为操作提示,引导用户进行操作。
// "Gamma": "\u4f3d\u9a6c\u503c",
// // "Flip horizontally" 翻译为 "水平翻转",在图片编辑、图形处理等场景下,当用户想要将元素在水平方向上进行镜像翻转时,
// // 对应的操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行水平翻转操作。
// "Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
// // "Resize" 翻译为 "调整大小",常用于图片、表格、窗口等元素改变尺寸大小相关操作的提示文本,
// // 对应的大小调整手柄、输入尺寸数值的框等地方会显示该文本作为操作提示,引导用户进行大小调整操作。
// "Resize": "\u8c03\u6574\u5927\u5c0f",
// // "Sharpen" 翻译为 "锐化",在图片编辑中,用于提高图片清晰度、使图像边缘更锐利等相关操作时,
// // 对应的锐化操作按钮、调整参数滑块等地方会显示该文本作为操作提示,引导用户进行锐化操作。
// "Sharpen": "\u9510\u5316",
// // "Zoom out" 翻译为 "缩小",与 "Zoom in" 相对,常用于图片查看、文档页面查看等场景下,当用户想要将显示内容缩小以便查看整体情况时,
// // 对应的操作按钮(如放大镜加 - 图标按钮等)会显示该文本作为操作提示,引导用户进行缩小操作。
// "Zoom out": "\u7f29\u5c0f",
// // "Image options" 翻译为 "图片选项",在图片相关功能中,用于展开更多关于图片的设置(如样式、格式、链接等各种设置)时,
// // 对应的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看和调整图片的各项具体设置。
// "Image options": "\u56fe\u7247\u9009\u9879",
// // "Apply" 翻译为 "应用",常用于当用户在设置界面调整好各项参数、选项后,点击该按钮来使设置生效,
// // 对应的操作按钮上会显示该文本作为提示,告知用户点击此按钮可将当前设置应用到相应对象上。
// "Apply": "\u5e94\u7528",
// // "Brightness" 翻译为 "亮度",在图片编辑、显示屏亮度调节等场景下,用于调整图片或屏幕显示内容的明亮程度相关操作时,
// // 对应的亮度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行亮度的调整。
// "Brightness": "\u4eae\u5ea6",
// // "Rotate clockwise" 翻译为 "顺时针旋转",在图片、图形等元素进行旋转操作时,用于提示用户按照顺时针方向旋转元素,
// // 对应的旋转操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行顺时针旋转操作。// "Color" 翻译为 "颜色",是一个比较通用的表示与颜色相关功能的文本,比如选择字体颜色、背景颜色、元素颜色等操作时,
// // 对应的菜单、对话框等地方会显示该文本作为操作提示,引导用户进行颜色相关的设置。
// "Color": "\u989c\u8272",
// // "Right to left" 翻译为 "从右到左",常用于文本排版、界面布局等涉及方向设置的功能场景,
// // 例如一些支持从右向左书写语言(如阿拉伯语等)的排版,或者切换元素排列方向等操作时会显示该文本提示。
// "Right to left": "\u4ece\u53f3\u5230\u5de6",
// // "Left to right" 翻译为 "从左到右",与上面对应,是常规的文本、元素排列方向设置相关功能显示的文本,
// // 比如设置文本阅读顺序、界面元素排列顺序等操作时会出现该文本作为提示信息。
// "Left to right": "\u4ece\u5de6\u5230\u53f3",
// // "Emoticons" 翻译为 "表情符号",在支持插入表情符号来丰富文档内容的编辑器中,
// // 当用户点击插入表情相关功能按钮、打开表情选择菜单等操作时会显示该文本作为提示,引导用户选择表情符号。
// "Emoticons": "\u8868\u60c5",
// // "Robots" 翻译为 "机器人",具体含义要根据具体应用场景判断,可能涉及到网页爬虫相关设置(搜索引擎机器人等)、
// // 自动化脚本模拟机器人操作等相关功能显示该文本作为相关提示内容。
// "Robots": "\u673a\u5668\u4eba",
// // "Document properties" 翻译为 "文档属性",常用于查看或编辑文档的各种元数据信息,如文档标题、作者、创建时间、
// // 关键词等相关功能的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看或修改这些属性。
// "Document properties": "\u6587\u6863\u5c5e\u6027",
// // "Title" 翻译为 "标题",用途广泛,可以表示文档的主标题、页面标题、章节标题等相关功能显示的文本,
// // 例如在设置文档标题栏显示内容、文章各级标题编辑等场景都会用到该文本提示。
// "Title": "\u6807\u9898",
// // "Keywords" 翻译为 "关键词",在文档属性设置、搜索引擎优化等场景下,用于填写、编辑能代表文档核心内容的关键词语,
// // 相关功能区域(如文档元数据编辑框等)会显示该文本作为操作提示,引导用户输入关键词。
// "Keywords": "\u5173\u952e\u8bcd",
// // "Encoding" 翻译为 "编码",常用于设置文档、网页等的字符编码格式(如 UTF-8、GBK 等)相关功能,
// // 在选择编码类型、查看当前编码等操作对应的菜单、对话框等地方会显示该文本作为提示信息。
// "Encoding": "\u7f16\u7801",
// // "Description" 翻译为 "描述",常用来表示对文档、网页等内容的简短说明文字,
// // 例如在文档元数据中填写文档简介、网页的描述信息用于搜索引擎展示摘要等场景会用到该文本提示用户输入相关内容。
// "Description": "\u63cf\u8ff0",
// // "Author" 翻译为 "作者",用于在文档属性等相关功能中填写、显示文档创作者的信息,
// // 对应功能区域(如文档作者编辑框等)会显示该文本作为操作提示,引导用户输入或查看作者名称。
// "Author": "\u4f5c\u8005",
// // "Fullscreen" 翻译为 "全屏",在应用程序、网页等提供全屏显示功能的地方,
// // 当用户点击全屏按钮、切换到全屏模式等操作时会显示该文本作为提示,告知用户当前进入了全屏状态或可进入全屏状态。
// "Fullscreen": "\u5168\u5c4f",
// // "Horizontal line" 翻译为 "水平线",在文档编辑、页面设计等场景下,用于插入、编辑水平分割线相关操作时,
// // 对应的菜单、按钮等地方会显示该文本作为操作提示,引导用户进行插入或修改水平线的操作。
// "Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
// // "Horizontal space" 翻译为 "水平间距",常用于调整页面元素、文本段落等在水平方向上的间隔距离相关功能,
// // 例如在排版设置中修改元素之间的水平空白大小等操作时会显示该文本作为操作提示。
// "Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
// // "Insert/edit image" 翻译为 "插入/编辑图片",在文档编辑、网页内容编辑等场景下,
// // 当用户想要添加一张新图片或者对已有的图片进行修改编辑(如调整大小、更换图片等操作)时,
// // 对应的操作按钮、菜单选项等地方会显示该文本作为提示,引导用户进行相应操作。
// "Insert/edit image": "\u63d2\u5165/\u7f16\u8f91\u56fe\u7247",
// // "General" 翻译为 "常规",常用于表示一组设置选项中的通用、基础设置部分,
// // 例如在软件的设置对话框中,会将一些常见的、基本的设置分类到 "常规" 选项卡下,对应的选项卡标题会显示该文本作为提示。
// "General": "\u666e\u901a",
// // "Advanced" 翻译为 "高级",与 "常规" 相对,通常用于存放更复杂、专业、进阶的设置选项,
// // 只有熟悉相关功能或者有特定需求的用户才会去调整这些设置,在设置界面中对应的选项卡标题等地方会显示该文本作为提示。
// "Advanced": "\u9ad8\u7ea7",
// // "Source" 翻译为 "来源",在很多场景下有不同含义,比如图片、视频等媒体资源的原始地址(来源链接),
// // 或者代码片段的引用来源等相关功能显示该文本作为提示信息,引导用户填写或查看相应的来源信息。
// "Source": "\u5730\u5740",
// // "Border" 翻译为 "边框",常用于设置页面元素(如图片、表格、文本框等)的边框样式、宽度、颜色等相关功能,
// // 对应的边框设置菜单、对话框等地方会显示该文本作为操作提示,引导用户进行边框相关的设置。
// "Border": "\u8fb9\u6846",
// // "Constrain proportions" 翻译为 "保持比例",在调整图片、图形等元素大小时,
// // 如果希望元素的宽高比例保持不变(避免拉伸变形),相关操作按钮、设置选项等地方会显示该文本作为提示,
// // 告知用户当前设置可保持元素原有比例进行缩放操作。
// "Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
// // "Vertical space" 翻译为 "垂直间距",类似于 "Horizontal space",不过是用于调整页面元素、文本段落等在垂直方向上的间隔距离相关功能,
// // 比如在排版中设置行间距、元素上下间隔等操作时会显示该文本作为操作提示。
// "Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
// // "Image description" 翻译为 "图片描述",常用于为图片添加说明性文字,
// // 比如在网页中方便搜索引擎识别图片内容、辅助视障人士理解图片含义等场景下,会有填写图片描述的功能,对应功能区域会显示该文本提示。
// "Image description": "\u56fe\u7247\u63cf\u8ff0",
// // "Style" 翻译为 "样式",在文档编辑、网页开发等场景下应用广泛,用于设置文本、元素的各种外观样式(如字体样式、颜色样式、布局样式等),
// // 对应的样式设置菜单、选项框等地方会显示该文本作为操作提示,引导用户进行样式相关的选择和设置。
// "Style": "\u6837\u5f0f",
// // "Dimensions" 翻译为 "尺寸",常用于查看或修改图片、表格、页面区域等元素的大小(宽度和高度)相关功能,
// // 例如在调整元素大小时,对应的尺寸输入框、调整按钮等地方会显示该文本作为操作提示,引导用户操作。
// "Dimensions": "\u5927\u5c0f",
// // "Insert image" 翻译为 "插入图片",是一个比较简洁明确的提示文本,用于在文档、网页等内容中添加新图片时,
// // 对应的操作按钮、菜单选项等地方会显示该文本,引导用户进行插入图片的操作。
// "Insert image": "\u63d2\u5165\u56fe\u7247",
// // "Image" 翻译为 "图片",是一个通用表示图片相关功能的文本,比如在图片管理、选择图片资源等场景下,
// // 对应的菜单、列表等地方会显示该文本作为提示,让用户明确当前操作涉及的对象是图片。
// "Image": "\u56fe\u7247",
// // "Zoom in" 翻译为 "放大",常用于图片查看、文档页面查看等场景下,当用户想要将显示内容放大以便查看细节时,
// // 对应的操作按钮(如放大镜加 + 图标按钮等)会显示该文本作为操作提示,引导用户进行放大操作。
// "Zoom in": "\u653e\u5927",
// // "Contrast" 翻译为 "对比度",在图片编辑、显示设置等场景下,用于调整图片或屏幕显示内容的对比度相关操作时,
// // 对应的对比度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行对比度的调整。
// "Contrast": "\u5bf9\u6bd4\u5ea6",
// // "Back" 翻译为 "后退",常用于页面导航、操作历史回退等场景,比如在浏览器中返回上一页、
// // 软件中撤销上一步操作回到之前的状态等操作时,对应的按钮等地方会显示该文本作为操作提示。
// "Back": "\u540e\u9000",
// // "Gamma" 翻译为 "伽马值",在涉及图像、显示屏等颜色校正、色彩调整相关功能中,伽马值是一个重要参数,
// // 当用户调整伽马值来改变显示效果时,对应的设置区域会显示该文本作为操作提示,引导用户进行操作。
// "Gamma": "\u4f3d\u9a6c\u503c",
// // "Flip horizontally" 翻译为 "水平翻转",在图片编辑、图形处理等场景下,当用户想要将元素在水平方向上进行镜像翻转时,
// // 对应的操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行水平翻转操作。
// "Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
// // "Resize" 翻译为 "调整大小",常用于图片、表格、窗口等元素改变尺寸大小相关操作的提示文本,
// // 对应的大小调整手柄、输入尺寸数值的框等地方会显示该文本作为操作提示,引导用户进行大小调整操作。
// "Resize": "\u8c03\u6574\u5927\u5c0f",
// // "Sharpen" 翻译为 "锐化",在图片编辑中,用于提高图片清晰度、使图像边缘更锐利等相关操作时,
// // 对应的锐化操作按钮、调整参数滑块等地方会显示该文本作为操作提示,引导用户进行锐化操作。
// "Sharpen": "\u9510\u5316",
// // "Zoom out" 翻译为 "缩小",与 "Zoom in" 相对,常用于图片查看、文档页面查看等场景下,当用户想要将显示内容缩小以便查看整体情况时,
// // 对应的操作按钮(如放大镜加 - 图标按钮等)会显示该文本作为操作提示,引导用户进行缩小操作。
// "Zoom out": "\u7f29\u5c0f",
// // "Image options" 翻译为 "图片选项",在图片相关功能中,用于展开更多关于图片的设置(如样式、格式、链接等各种设置)时,
// // 对应的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看和调整图片的各项具体设置。
// "Image options": "\u56fe\u7247\u9009\u9879",
// // "Apply" 翻译为 "应用",常用于当用户在设置界面调整好各项参数、选项后,点击该按钮来使设置生效,
// // 对应的操作按钮上会显示该文本作为提示,告知用户点击此按钮可将当前设置应用到相应对象上。
// "Apply": "\u5e94\u7528",
// // "Brightness" 翻译为 "亮度",在图片编辑、显示屏亮度调节等场景下,用于调整图片或屏幕显示内容的明亮程度相关操作时,
// // 对应的亮度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行亮度的调整。
// "Brightness": "\u4eae\u5ea6",
// // "Rotate clockwise" 翻译为 "顺时针旋转",在图片、图形等元素进行旋转操作时,用于提示用户按照顺时针方向旋转元素,
// // 对应的旋转操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行顺时针旋转操作。// "Color" 翻译为 "颜色",是一个比较通用的表示与颜色相关功能的文本,比如选择字体颜色、背景颜色、元素颜色等操作时,
// // 对应的菜单、对话框等地方会显示该文本作为操作提示,引导用户进行颜色相关的设置。
// "Color": "\u989c\u8272",
// // "Right to left" 翻译为 "从右到左",常用于文本排版、界面布局等涉及方向设置的功能场景,
// // 例如一些支持从右向左书写语言(如阿拉伯语等)的排版,或者切换元素排列方向等操作时会显示该文本提示。
// "Right to left": "\u4ece\u53f3\u5230\u5de6",
// // "Left to right" 翻译为 "从左到右",与上面对应,是常规的文本、元素排列方向设置相关功能显示的文本,
// // 比如设置文本阅读顺序、界面元素排列顺序等操作时会出现该文本作为提示信息。
// "Left to right": "\u4ece\u5de6\u5230\u53f3",
// // "Emoticons" 翻译为 "表情符号",在支持插入表情符号来丰富文档内容的编辑器中,
// // 当用户点击插入表情相关功能按钮、打开表情选择菜单等操作时会显示该文本作为提示,引导用户选择表情符号。
// "Emoticons": "\u8868\u60c5",
// // "Robots" 翻译为 "机器人",具体含义要根据具体应用场景判断,可能涉及到网页爬虫相关设置(搜索引擎机器人等)、
// // 自动化脚本模拟机器人操作等相关功能显示该文本作为相关提示内容。
// "Robots": "\u673a\u5668\u4eba",
// // "Document properties" 翻译为 "文档属性",常用于查看或编辑文档的各种元数据信息,如文档标题、作者、创建时间、
// // 关键词等相关功能的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看或修改这些属性。
// "Document properties": "\u6587\u6863\u5c5e\u6027",
// // "Title" 翻译为 "标题",用途广泛,可以表示文档的主标题、页面标题、章节标题等相关功能显示的文本,
// // 例如在设置文档标题栏显示内容、文章各级标题编辑等场景都会用到该文本提示。
// "Title": "\u6807\u9898",
// // "Keywords" 翻译为 "关键词",在文档属性设置、搜索引擎优化等场景下,用于填写、编辑能代表文档核心内容的关键词语,
// // 相关功能区域(如文档元数据编辑框等)会显示该文本作为操作提示,引导用户输入关键词。
// "Keywords": "\u5173\u952e\u8bcd",
// // "Encoding" 翻译为 "编码",常用于设置文档、网页等的字符编码格式(如 UTF-8、GBK 等)相关功能,
// // 在选择编码类型、查看当前编码等操作对应的菜单、对话框等地方会显示该文本作为提示信息。
// "Encoding": "\u7f16\u7801",
// // "Description" 翻译为 "描述",常用来表示对文档、网页等内容的简短说明文字,
// // 例如在文档元数据中填写文档简介、网页的描述信息用于搜索引擎展示摘要等场景会用到该文本提示用户输入相关内容。
// "Description": "\u63cf\u8ff0",
// // "Author" 翻译为 "作者",用于在文档属性等相关功能中填写、显示文档创作者的信息,
// // 对应功能区域(如文档作者编辑框等)会显示该文本作为操作提示,引导用户输入或查看作者名称。
// "Author": "\u4f5c\u8005",
// // "Fullscreen" 翻译为 "全屏",在应用程序、网页等提供全屏显示功能的地方,
// // 当用户点击全屏按钮、切换到全屏模式等操作时会显示该文本作为提示,告知用户当前进入了全屏状态或可进入全屏状态。
// "Fullscreen": "\u5168\u5c4f",
// // "Horizontal line" 翻译为 "水平线",在文档编辑、页面设计等场景下,用于插入、编辑水平分割线相关操作时,
// // 对应的菜单、按钮等地方会显示该文本作为操作提示,引导用户进行插入或修改水平线的操作。
// "Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
// // "Horizontal space" 翻译为 "水平间距",常用于调整页面元素、文本段落等在水平方向上的间隔距离相关功能,
// // 例如在排版设置中修改元素之间的水平空白大小等操作时会显示该文本作为操作提示。
// "Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
// // "Insert/edit image" 翻译为 "插入/编辑图片",在文档编辑、网页内容编辑等场景下,
// // 当用户想要添加一张新图片或者对已有的图片进行修改编辑(如调整大小、更换图片等操作)时,
// // 对应的操作按钮、菜单选项等地方会显示该文本作为提示,引导用户进行相应操作。
// "Insert/edit image": "\u63d2\u5165/\u7f16\u8f91\u56fe\u7247",
// // "General" 翻译为 "常规",常用于表示一组设置选项中的通用、基础设置部分,
// // 例如在软件的设置对话框中,会将一些常见的、基本的设置分类到 "常规" 选项卡下,对应的选项卡标题会显示该文本作为提示。
// "General": "\u666e\u901a",
// // "Advanced" 翻译为 "高级",与 "常规" 相对,通常用于存放更复杂、专业、进阶的设置选项,
// // 只有熟悉相关功能或者有特定需求的用户才会去调整这些设置,在设置界面中对应的选项卡标题等地方会显示该文本作为提示。
// "Advanced": "\u9ad8\u7ea7",
// // "Source" 翻译为 "来源",在很多场景下有不同含义,比如图片、视频等媒体资源的原始地址(来源链接),
// // 或者代码片段的引用来源等相关功能显示该文本作为提示信息,引导用户填写或查看相应的来源信息。
// "Source": "\u5730\u5740",
// // "Border" 翻译为 "边框",常用于设置页面元素(如图片、表格、文本框等)的边框样式、宽度、颜色等相关功能,
// // 对应的边框设置菜单、对话框等地方会显示该文本作为操作提示,引导用户进行边框相关的设置。
// "Border": "\u8fb9\u6846",
// // "Constrain proportions" 翻译为 "保持比例",在调整图片、图形等元素大小时,
// // 如果希望元素的宽高比例保持不变(避免拉伸变形),相关操作按钮、设置选项等地方会显示该文本作为提示,
// // 告知用户当前设置可保持元素原有比例进行缩放操作。
// "Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
// // "Vertical space" 翻译为 "垂直间距",类似于 "Horizontal space",不过是用于调整页面元素、文本段落等在垂直方向上的间隔距离相关功能,
// // 比如在排版中设置行间距、元素上下间隔等操作时会显示该文本作为操作提示。
// "Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
// // "Image description" 翻译为 "图片描述",常用于为图片添加说明性文字,
// // 比如在网页中方便搜索引擎识别图片内容、辅助视障人士理解图片含义等场景下,会有填写图片描述的功能,对应功能区域会显示该文本提示。
// "Image description": "\u56fe\u7247\u63cf\u8ff0",
// // "Style" 翻译为 "样式",在文档编辑、网页开发等场景下应用广泛,用于设置文本、元素的各种外观样式(如字体样式、颜色样式、布局样式等),
// // 对应的样式设置菜单、选项框等地方会显示该文本作为操作提示,引导用户进行样式相关的选择和设置。
// "Style": "\u6837\u5f0f",
// // "Dimensions" 翻译为 "尺寸",常用于查看或修改图片、表格、页面区域等元素的大小(宽度和高度)相关功能,
// // 例如在调整元素大小时,对应的尺寸输入框、调整按钮等地方会显示该文本作为操作提示,引导用户操作。
// "Dimensions": "\u5927\u5c0f",
// // "Insert image" 翻译为 "插入图片",是一个比较简洁明确的提示文本,用于在文档、网页等内容中添加新图片时,
// // 对应的操作按钮、菜单选项等地方会显示该文本,引导用户进行插入图片的操作。
// "Insert image": "\u63d2\u5165\u56fe\u7247",
// // "Image" 翻译为 "图片",是一个通用表示图片相关功能的文本,比如在图片管理、选择图片资源等场景下,
// // 对应的菜单、列表等地方会显示该文本作为提示,让用户明确当前操作涉及的对象是图片。
// "Image": "\u56fe\u7247",
// // "Zoom in" 翻译为 "放大",常用于图片查看、文档页面查看等场景下,当用户想要将显示内容放大以便查看细节时,
// // 对应的操作按钮(如放大镜加 + 图标按钮等)会显示该文本作为操作提示,引导用户进行放大操作。
// "Zoom in": "\u653e\u5927",
// // "Contrast" 翻译为 "对比度",在图片编辑、显示设置等场景下,用于调整图片或屏幕显示内容的对比度相关操作时,
// // 对应的对比度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行对比度的调整。
// "Contrast": "\u5bf9\u6bd4\u5ea6",
// // "Back" 翻译为 "后退",常用于页面导航、操作历史回退等场景,比如在浏览器中返回上一页、
// // 软件中撤销上一步操作回到之前的状态等操作时,对应的按钮等地方会显示该文本作为操作提示。
// "Back": "\u540e\u9000",
// // "Gamma" 翻译为 "伽马值",在涉及图像、显示屏等颜色校正、色彩调整相关功能中,伽马值是一个重要参数,
// // 当用户调整伽马值来改变显示效果时,对应的设置区域会显示该文本作为操作提示,引导用户进行操作。
// "Gamma": "\u4f3d\u9a6c\u503c",
// // "Flip horizontally" 翻译为 "水平翻转",在图片编辑、图形处理等场景下,当用户想要将元素在水平方向上进行镜像翻转时,
// // 对应的操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行水平翻转操作。
// "Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
// // "Resize" 翻译为 "调整大小",常用于图片、表格、窗口等元素改变尺寸大小相关操作的提示文本,
// // 对应的大小调整手柄、输入尺寸数值的框等地方会显示该文本作为操作提示,引导用户进行大小调整操作。
// "Resize": "\u8c03\u6574\u5927\u5c0f",
// // "Sharpen" 翻译为 "锐化",在图片编辑中,用于提高图片清晰度、使图像边缘更锐利等相关操作时,
// // 对应的锐化操作按钮、调整参数滑块等地方会显示该文本作为操作提示,引导用户进行锐化操作。
// "Sharpen": "\u9510\u5316",
// // "Zoom out" 翻译为 "缩小",与 "Zoom in" 相对,常用于图片查看、文档页面查看等场景下,当用户想要将显示内容缩小以便查看整体情况时,
// // 对应的操作按钮(如放大镜加 - 图标按钮等)会显示该文本作为操作提示,引导用户进行缩小操作。
// "Zoom out": "\u7f29\u5c0f",
// // "Image options" 翻译为 "图片选项",在图片相关功能中,用于展开更多关于图片的设置(如样式、格式、链接等各种设置)时,
// // 对应的菜单、对话框等地方会显示该文本作为操作入口提示,引导用户查看和调整图片的各项具体设置。
// "Image options": "\u56fe\u7247\u9009\u9879",
// // "Apply" 翻译为 "应用",常用于当用户在设置界面调整好各项参数、选项后,点击该按钮来使设置生效,
// // 对应的操作按钮上会显示该文本作为提示,告知用户点击此按钮可将当前设置应用到相应对象上。
// "Apply": "\u5e94\u7528",
// // "Brightness" 翻译为 "亮度",在图片编辑、显示屏亮度调节等场景下,用于调整图片或屏幕显示内容的明亮程度相关操作时,
// // 对应的亮度调整滑块、输入框等地方会显示该文本作为操作提示,引导用户进行亮度的调整。
// "Brightness": "\u4eae\u5ea6",
// // "Rotate clockwise" 翻译为 "顺时针旋转",在图片、图形等元素进行旋转操作时,用于提示用户按照顺时针方向旋转元素,
// // 对应的旋转操作按钮、菜单选项等地方会显示该文本作为操作提示,引导用户进行顺时针旋转操作。//
"Print": "\u6253\u5370", "Print": "\u6253\u5370",
"Save": "\u4fdd\u5b58", "Save": "\u4fdd\u5b58",
"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.", "Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
@ -227,4 +845,4 @@ tinymce.addI18n('zh_CN',{
"View": "\u89c6\u56fe", "View": "\u89c6\u56fe",
"Table": "\u8868\u683c", "Table": "\u8868\u683c",
"Format": "\u683c\u5f0f" "Format": "\u683c\u5f0f"
}); });

@ -1,121 +1,194 @@
<template>  <template>
<div> <!-- 创建一个 div 元素作为整个组件内容的包裹容器这是 HTML 中常用的用于组织和划分页面结构的方式 -->
<el-upload <div>
:action="useOss?ossUploadUrl:minioUploadUrl" <!-- el-upload 组件用于实现文件上传功能它是基于 Element UI 库提供的组件方便在 Vue 项目中快速集成文件上传交互功能 -->
:data="useOss?dataObj:null" <el-upload
list-type="picture-card" <!-- 通过三元表达式根据 useOss 的值来动态决定上传文件的 action 属性值即上传文件的请求地址 -->
:file-list="fileList" <!-- 如果 useOss true就使用 ossUploadUrl 作为上传地址这通常意味着要将文件上传到阿里云 OSSObject Storage Service对象存储服务服务器上 -->
:before-upload="beforeUpload" <!-- 如果 useOss false则使用 minioUploadUrl 作为上传地址MinIO 也是一种常用的对象存储服务这里可以根据实际需求切换使用不同的存储服务来上传文件 -->
:on-remove="handleRemove" :action="useOss?ossUploadUrl:minioUploadUrl"
:on-success="handleUploadSuccess" <!-- 同样基于 useOss 的值来决定是否向上传请求中传递额外的数据以对象形式 -->
:on-preview="handlePreview" <!-- useOss true dataObj 作为额外的数据传递给上传请求这些数据一般包含了与文件上传到 OSS 相关的配置信息例如上传策略签名等用于服务器验证上传的合法性 -->
:limit="maxCount" <!-- useOss false 不传递额外的数据即设置为 null -->
:on-exceed="handleExceed" :data="useOss?dataObj:null"
> <!-- 设置文件列表在页面上的展示类型为 "picture-card"这种类型的展示效果通常是以卡片形式呈现已上传的文件在这里主要是图片文件 -->
<i class="el-icon-plus"></i> <!-- 每个文件会以一个独立的卡片样式展示方便用户直观地查看已上传的文件并且可以进行相应的操作如删除预览等 -->
</el-upload> list-type="picture-card"
<el-dialog :visible.sync="dialogVisible"> <!-- 通过绑定计算属性 fileList将已上传文件的相关信息以特定格式传递给 el-upload 组件以便组件能够正确展示已上传的文件列表 -->
<img width="100%" :src="dialogImageUrl" alt=""> <!-- 计算属性 fileList 会根据组件接收到的相关数据进行处理转化为 el-upload 组件可识别的格式后面会在计算属性部分详细说明 -->
</el-dialog> :file-list="fileList"
</div> <!-- 绑定一个名为 before-upload 的回调函数该函数会在每个文件上传之前被触发 -->
<!-- 可以在这个函数中进行一些前置的验证准备工作例如当使用阿里云 OSS 上传时需要在这里获取并设置相关的上传策略等配置信息决定文件是否可以进行上传 -->
:before-upload="beforeUpload"
<!-- 绑定一个名为 on-remove 的回调函数当用户在已展示的文件列表中删除某个文件时这个函数会被触发 -->
<!-- 用于处理文件删除后的相关逻辑比如通知父组件文件列表发生了变化让父组件可以相应地更新页面上显示的文件信息等 -->
:on-remove="handleRemove"
<!-- 绑定一个名为 on-success 的回调函数在文件上传成功后该函数会被触发 -->
<!-- 可以在这个函数里进行后续的操作例如更新文件列表数据通知父组件上传成功的消息以及根据上传结果进行一些页面展示相关的调整等 -->
:on-success="handleUploadSuccess"
<!-- 绑定一个名为 on-preview 的回调函数当用户点击已上传的文件进行预览操作时这个函数会被触发 -->
<!-- 主要用于实现文件的预览功能比如打开一个对话框展示图片的大图等操作 -->
:on-preview="handlePreview"
<!-- 通过绑定 maxCount 属性来设置允许上传的文件最大数量这个属性的值来源于组件通过 props 接收的父组件传递过来的 maxCount -->
<!-- 限制用户最多只能上传指定数量的文件若用户尝试上传超过此数量的文件会触发相应的超出限制处理逻辑 -->
:limit="maxCount"
<!-- 绑定一个名为 on-exceed 的回调函数当用户上传的文件数量超过了通过 :limit 设置的最大数量时该函数会被触发 -->
<!-- 用于给出相应的提示信息告知用户上传文件数量超出了限制让用户知晓当前的操作不符合规定 -->
:on-exceed="handleExceed"
>
<!-- el-upload 组件内部添加一个图标元素使用 el-icon-plus 这个类来展示一个加号图标 -->
<!-- 通常这个加号图标在界面上用于提示用户可以点击此处进行文件上传操作是一种常见的交互设计方式 -->
<i class="el-icon-plus"></i>
</el-upload>
<!-- el-dialog 组件用于创建一个对话框用于展示图片的预览功能它也是 Element UI 库中的组件方便实现弹出式的展示界面 -->
<el-dialog :visible.sync="dialogVisible">
<!-- 在对话框内部放置一个 img 元素用于展示要预览的图片 -->
<!-- 通过绑定 src 属性为 dialogImageUrl根据这个属性的值来确定要展示的图片的路径从而正确显示相应的图片内容 -->
<!-- 设置图片的宽度为 100%让图片能够自适应对话框的宽度完整地展示出来 -->
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</div>
</template> </template>
<script> <script>
import {policy} from '@/api/oss' // '@/api/oss' policy OSS Policy -->
// 访 OSS
import {policy} from '@/api/oss';
export default { // Vue Vue 使使
name: 'multiUpload', export default {
props: { // Vue 'multiUpload'便
// name: 'multiUpload',
value: Array, // props -->
// props: {
maxCount:{ // value -->
type:Number, // 便 -->
default:5 value: Array,
} // maxCount -->
}, // Number 5 5 -->
data() { maxCount: {
return { type: Number,
dataObj: { default: 5
policy: '', }
signature: '',
key: '',
ossaccessKeyId: '',
dir: '',
host: ''
}, },
dialogVisible: false, data() {
dialogImageUrl:null, return {
useOss:false, //使oss->true;使MinIO->false // dataObj 使 OSS -->
ossUploadUrl:'http://macro-oss.oss-cn-shenzhen.aliyuncs.com', // policy signature ossaccessKeyId 访 ID OSS -->
minioUploadUrl:'http://localhost:8080/minio/upload', dataObj: {
}; policy: '',
}, signature: '',
computed: { key: '',
fileList() { ossaccessKeyId: '',
let fileList=[]; dir: '',
for(let i=0;i<this.value.length;i++){ host: ''
fileList.push({url:this.value[i]}); },
} // dialogVisible false -->
return fileList; // true使 -->
} dialogVisible: false,
}, // dialogImageUrl null -->
methods: { // 便 img -->
emitInput(fileList) { dialogImageUrl: null,
let value=[]; // useOss 使 OSS true MinIO false -->
for(let i=0;i<fileList.length;i++){ // -->
value.push(fileList[i].url); useOss: false,
} // ossUploadUrl OSS -->
this.$emit('input', value) // OSS useOss true 使 OSS -->
}, ossUploadUrl: 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com',
handleRemove(file, fileList) { // minioUploadUrl MinIO -->
this.emitInput(fileList); // useOss false 使 MinIO MinIO -->
}, minioUploadUrl: 'http://localhost:8080/minio/upload',
handlePreview(file) { };
this.dialogVisible = true; },
this.dialogImageUrl=file.url; computed: {
}, // fileList value -->
beforeUpload(file) { // value url fileList fileList -->
let _self = this; // fileList el-upload -->
if(!this.useOss){ fileList() {
//使oss let fileList = [];
return true; for (let i = 0; i < this.value.length; i++) {
} fileList.push({url: this.value[i]});
return new Promise((resolve, reject) => { }
policy().then(response => { return fileList;
_self.dataObj.policy = response.data.policy; }
_self.dataObj.signature = response.data.signature; },
_self.dataObj.ossaccessKeyId = response.data.accessKeyId; methods: {
_self.dataObj.key = response.data.dir + '/${filename}'; // emitInput 'input' -->
_self.dataObj.dir = response.data.dir; // 使 -->
_self.dataObj.host = response.data.host; emitInput(fileList) {
resolve(true) let value = [];
}).catch(err => { for (let i = 0; i < fileList.length; i++) {
console.log(err) value.push(fileList[i].url);
reject(false) }
}) this.$emit('input', value)
}) },
}, // handleRemove -->
handleUploadSuccess(res, file) { // el-upload emitInput fileList -->
let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name; // -->
if(!this.useOss){ handleRemove(file, fileList) {
//使oss this.emitInput(fileList);
url = res.data.url; },
// handlePreview -->
// dialogVisible true使 -->
// file.url dialogImageUrl 便 img -->
handlePreview(file) {
this.dialogVisible = true;
this.dialogImageUrl = file.url;
},
// beforeUpload -->
// 使 OSS useOss false true -->
// 使 OSSuseOss true policy -->
// dataObj resolve(true) -->
// catch reject(false) -->
beforeUpload(file) {
let _self = this;
if (!this.useOss) {
// 使 oss
return true;
}
return new Promise((resolve, reject) => {
policy().then(response => {
_self.dataObj.policy = response.data.policy;
_self.dataObj.signature = response.data.signature;
_self.dataObj.ossaccessKeyId = response.data.accessKeyId;
_self.dataObj.key = response.data.dir + '/${filename}';
_self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host;
resolve(true)
}).catch(err => {
console.log(err)
reject(false)
})
})
},
// handleUploadSuccess -->
// 使 OSS useOss -->
// 使 OSS dataObj.hostdataObj.dirfile.name访 -->
// 使 OSS使res.data.url -->
// 14342 fileList -->
// emitInput 便 -->
handleUploadSuccess(res, file) {
let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name;
if (!this.useOss) {
// 使 oss
url = res.data.url;
}
this.fileList.push({name: file.name, url: url});
this.emitInput(this.fileList);
},
// handleExceed -->
// maxCount -->
// $message 使 this.maxCount -->
// 1000 1 -->
handleExceed(files, fileList) {
this.$message({
message: '最多只能上传' + this.maxCount + '张图片',
type: 'warning',
duration: 1000
});
},
} }
this.fileList.push({name: file.name,url:url});
this.emitInput(this.fileList);
},
handleExceed(files, fileList) {
this.$message({
message: '最多只能上传'+this.maxCount+'张图片',
type: 'warning',
duration:1000
});
},
} }
}
</script> </script>
<style> <style>
</style> </style>

@ -1,30 +1,52 @@
// 导入Vue框架
import Vue from 'vue' import Vue from 'vue'
import 'normalize.css/normalize.css'// A modern alternative to CSS resets // 导入normalize.css这是一种现代的CSS重置替代方案
import 'normalize.css/normalize.css'
import ElementUI from 'element-ui' // 导入Element UI库
import 'element-ui/lib/theme-chalk/index.css' import ElementUI from 'element - ui'
import locale from 'element-ui/lib/locale/lang/zh-CN' // lang i18n // 导入Element UI的样式文件这里使用的是主题为chalk的样式
import VCharts from 'v-charts' import 'element - ui/lib/theme - chalk/index.css'
// 导入Element UI的中文语言包用于国际化i18n
import locale from 'element - ui/lib/locale/lang/zh - CN'
import '@/styles/index.scss' // global css // 导入V - Charts库用于图表绘制
import VCharts from 'v - charts'
// 导入项目中自定义的全局样式文件
import '@/styles/index.scss'
// 导入应用的根组件App
import App from './App' import App from './App'
// 导入路由配置文件
import router from './router' import router from './router'
// 导入Vuex的store配置文件
import store from './store' import store from './store'
import '@/icons' // icon // 导入项目中的图标相关配置(可能是注册全局图标组件等操作)
import '@/permission' // permission control import '@/icons'
// 导入权限控制相关的配置(可能是实现路由权限、组件权限等功能)
import '@/permission'
// 使用Element UI并传入中文语言包配置
Vue.use(ElementUI, { locale }) Vue.use(ElementUI, { locale })
// 使用V - Charts库
Vue.use(VCharts) Vue.use(VCharts)
// 设置在生产环境下不显示Vue的提示信息
Vue.config.productionTip = false Vue.config.productionTip = false
// 创建一个Vue实例
new Vue({ new Vue({
// 挂载点选择id为app的DOM元素
el: '#app', el: '#app',
// 使用导入的路由配置
router, router,
// 使用导入的store配置
store, store,
// 使用App组件作为模板
template: '<App/>', template: '<App/>',
// 注册App组件
components: { App } components: { App }
}) })

@ -1,46 +1,79 @@
// 导入路由配置文件,这里的路由配置可能定义了应用中的各个页面路径和对应的组件等信息
import router from './router' import router from './router'
// 导入Vuex的store配置文件store通常用于管理应用中的状态数据
import store from './store' import store from './store'
import NProgress from 'nprogress' // Progress 进度条 // 导入NProgress库用于在页面跳转时显示进度条
import 'nprogress/nprogress.css'// Progress 进度条样式 import NProgress from 'nprogress'
import { Message } from 'element-ui' // 导入NProgress的样式文件这样才能正确显示进度条样式
import { getToken } from '@/utils/auth' // 验权 import 'nprogress/nprogress.css'
// 从element - ui中导入Message组件用于显示提示信息
import { Message } from 'element - ui'
// 从 @/utils/auth文件中导入getToken函数该函数可能用于获取用户的登录令牌token用于验证用户是否已登录
import { getToken } from '@/utils/auth'
const whiteList = ['/login'] // 不重定向白名单 // 定义一个数组,其中包含不需要进行重定向的路径(白名单),这里只有'/login'路径在白名单中
const whiteList = ['/login']
// 注册一个全局的路由前置守卫beforeEach在每次路由跳转前都会执行这个函数
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// 启动NProgress进度条表示路由跳转开始
NProgress.start() NProgress.start()
// 判断用户是否有登录令牌token
if (getToken()) { if (getToken()) {
// 如果用户已登录且要访问的路径是'/login'(登录页)
if (to.path === '/login') { if (to.path === '/login') {
// 则重定向到应用的首页('/'
next({ path: '/' }) next({ path: '/' })
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it // 手动结束NProgress进度条因为如果当前页是首页不会触发afterEach钩子函数
NProgress.done()
} else { } else {
// 如果用户已登录且要访问的不是登录页
// 检查store中是否已经获取到用户角色信息如果没有获取到说明可能是首次登录或者角色信息丢失
if (store.getters.roles.length === 0) { if (store.getters.roles.length === 0) {
store.dispatch('GetInfo').then(res => { // 拉取用户信息 // 从store中分发dispatch一个名为'GetInfo'的action这个action可能用于从后端获取用户信息
let menus=res.data.menus; store.dispatch('GetInfo').then(res => {
let username=res.data.username; // 获取从后端返回的用户信息中的菜单menus数据和用户名username数据
store.dispatch('GenerateRoutes', { menus,username }).then(() => { // 生成可访问的路由表 let menus = res.data.menus;
router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表 let username = res.data.username;
next({ ...to, replace: true }) // 根据获取到的菜单数据和用户名在store中分发dispatch一个名为'GenerateRoutes'的action来生成用户可访问的路由表
store.dispatch('GenerateRoutes', { menus, username }).then(() => {
// 将生成的可访问路由表动态添加到路由实例中
router.addRoutes(store.getters.addRoutes);
// 使用传入的to对象要访问的路由进行跳转并设置replace为true表示替换当前历史记录而不是添加新记录
next({...to, replace: true })
}) })
}).catch((err) => { }).catch((err) => {
// 如果在获取用户信息过程中出错
// 先从store中分发dispatch一个名为'FedLogOut'的action这个action可能用于执行用户登出操作
store.dispatch('FedLogOut').then(() => { store.dispatch('FedLogOut').then(() => {
// 显示错误提示信息,提示用户验证失败,需要重新登录
Message.error(err || 'Verification failed, please login again') Message.error(err || 'Verification failed, please login again')
// 重定向到应用的首页('/'
next({ path: '/' }) next({ path: '/' })
}) })
}) })
} else { } else {
// 如果store中已经有用户角色信息直接放行允许用户访问目标路由
next() next()
} }
} }
} else { } else {
if (whiteList.indexOf(to.path) !== -1) { // 如果用户没有登录令牌token
// 检查要访问的路径是否在白名单中
if (whiteList.indexOf(to.path)!== -1) {
// 如果在白名单中,直接放行,允许用户访问目标路由
next() next()
} else { } else {
// 如果不在白名单中,重定向到登录页('/login'
next('/login') next('/login')
// 手动结束NProgress进度条
NProgress.done() NProgress.done()
} }
} }
}) })
// 注册一个全局的路由后置守卫afterEach在每次路由跳转完成后都会执行这个函数
router.afterEach(() => { router.afterEach(() => {
NProgress.done() // 结束Progress // 结束NProgress进度条表示路由跳转结束
NProgress.done()
}) })

@ -1,71 +1,113 @@
// 导入Vue构造函数Vue是Vue.js框架的核心用于创建Vue实例以及进行各种组件化开发相关操作
import Vue from 'vue' import Vue from 'vue'
// 导入Vue Router插件Vue Router用于在Vue.js应用中实现路由功能管理不同页面路由组件之间的切换和导航
import Router from 'vue-router' import Router from 'vue-router'
// 使用Vue.use方法来安装Vue Router插件使其能够在Vue应用中被使用这一步是必要的初始化操作
Vue.use(Router) Vue.use(Router)
/* Layout */ /* Layout */
// 从指定的路径 '../views/layout/Layout' 导入一个名为Layout的组件通常这个组件可能是整个应用的布局框架组件包含侧边栏、顶部导航栏等通用布局结构其他页面组件会在这个布局框架内进行展示
import Layout from '../views/layout/Layout' import Layout from '../views/layout/Layout'
/** /**
* 以下是一段注释用于对路由配置对象中的一些属性进行说明方便后续理解和维护路由配置
* hidden: true if `hidden:true` will not show in the sidebar(default is false) * hidden: true if `hidden:true` will not show in the sidebar(default is false)
* 解释如果路由配置中的 `hidden` 属性设置为 `true`则对应的路由对应的菜单项将不会显示在侧边栏中默认值为 `false`即默认会显示在侧边栏
*
* alwaysShow: true if set true, will always show the root menu, whatever its child routes length * alwaysShow: true if set true, will always show the root menu, whatever its child routes length
* if not set alwaysShow, only more than one route under the children * if not set alwaysShow, only more than one route under the children
* it will becomes nested mode, otherwise not show the root menu * it will becomes nested mode, otherwise not show the root menu
* 解释如果 `alwaysShow` 属性设置为 `true`无论该路由下的子路由数量是多少都会始终显示根菜单如果不设置这个属性只有当子路由数量大于1时才会呈现嵌套菜单模式显示根菜单以及子菜单否则不会显示根菜单
*
* redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb * redirect: noredirect if `redirect:noredirect` will no redirct in the breadcrumb
* 解释如果 `redirect` 属性设置为 `noredirect`在面包屑导航中不会进行重定向操作通常面包屑导航会根据路由跳转情况进行相应的更新显示这里可用于特殊情况的控制
*
* name:'router-name' the name is used by <keep-alive> (must set!!!) * name:'router-name' the name is used by <keep-alive> (must set!!!)
* 解释`name` 属性用于给路由命名这个名称会被 `<keep-alive>` 组件使用必须设置在进行组件缓存等相关功能时需要通过这个名称来识别路由对应的组件
*
* meta : { * meta : {
title: 'title' the name show in submenu and breadcrumb (recommend set) title: 'title' the name show in submenu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar, icon: 'svg-name' the icon show in the sidebar,
} }
* 解释`meta` 是一个自定义的元数据对象可以在路由配置中添加额外的信息其中 `title` 属性用于设置在子菜单和面包屑导航中显示的名称建议设置方便用户直观了解当前页面信息`icon` 属性用于指定在侧边栏中显示的图标方便通过图标区分不同的菜单功能
**/ **/
// 定义一个名为constantRouterMap的常量数组用于存放基础的、固定不变的路由配置信息这些路由通常是应用中始终存在且不需要动态加载的部分
export const constantRouterMap = [ export const constantRouterMap = [
// 定义一个登录页面的路由配置对象,路径为 '/login',对应的组件通过动态导入的方式加载(使用 () => import('@/views/login/index') 这种语法,会在实际访问该路由时才加载对应的组件代码,提高初始加载性能),并且设置 `hidden` 属性为 `true`,意味着这个登录页面不会显示在侧边栏中
{path: '/login', component: () => import('@/views/login/index'), hidden: true}, {path: '/login', component: () => import('@/views/login/index'), hidden: true},
// 定义一个404页面的路由配置对象路径为 '/404',同样通过动态导入加载对应的组件,也设置 `hidden` 属性为 `true`,该页面不会在侧边栏展示,一般用于处理未匹配到的路由情况
{path: '/404', component: () => import('@/views/404'), hidden: true}, {path: '/404', component: () => import('@/views/404'), hidden: true},
{ {
// 定义一个根路径(空字符串表示应用的根路径)的路由配置,它会作为应用的基础布局框架
path: '', path: '',
// 指定该根路径对应的组件为之前导入的Layout组件也就是整个页面的布局框架会围绕这个组件展开
component: Layout, component: Layout,
// 设置重定向路径为 '/home',意味着当访问根路径时,会自动跳转到 '/home' 这个子路径对应的页面
redirect: '/home', redirect: '/home',
// 在路由的元数据meta中设置标题为 '首页',图标为 'home',这两个信息会分别用于侧边栏菜单的显示和面包屑导航等地方的展示
meta: {title: '首页', icon: 'home'}, meta: {title: '首页', icon: 'home'},
children: [{ // 定义该路由下的子路由数组,每个子路由对应一个具体的页面组件
path: 'home', children: [
name: 'home', {
component: () => import('@/views/home/index'), // 子路由的路径为 'home',也就是在根路径下访问 'home' 会对应这个路由配置
meta: {title: '仪表盘', icon: 'dashboard'} path: 'home',
}, // 给这个路由命名为 'home',用于后续的路由导航、组件缓存等相关操作
{ name: 'home',
name: 'document', // 通过动态导入加载对应的组件,这里对应的是 '@/views/home/index' 这个页面组件,一般是应用的仪表盘页面之类的主页面
path: 'https://www.macrozheng.com', component: () => import('@/views/home/index'),
meta: {title: '学习教程', icon: 'document'} // 在子路由的元数据中设置标题为 '仪表盘',图标为 'dashboard',同样用于展示相关信息
}, meta: {title: '仪表盘', icon: 'dashboard'}
{ },
name: 'video', {
path: 'https://www.macrozheng.com/mall/catalog/mall_video.html', // 给这个路由命名为 'document',路径是一个外部链接 'https://www.macrozheng.com',可能用于跳转到外部学习教程网站
meta: {title: '视频教程', icon: 'video'} name: 'document',
}, path: 'https://www.macrozheng.com',
// 在元数据中设置标题为 '学习教程',图标为 'document',用于侧边栏等地方显示相关信息
meta: {title: '学习教程', icon: 'document'}
},
{
// 给这个路由命名为 'video',路径是另一个外部链接 'https://www.macrozheng.com/mall/catalog/mall_video.html',可能用于跳转到外部视频教程页面
name: 'video',
path: 'https://www.macrozheng.com/mall/catalog/mall_video.html',
// 在元数据中设置标题为 '视频教程',图标为 'video',用于展示相关信息
meta: {title: '视频教程', icon: 'video'}
},
] ]
} }
] ]
// 定义一个名为asyncRouterMap的常量数组用于存放需要动态加载的路由配置信息通常这些路由对应的模块可能比较大或者是根据用户权限等情况动态决定是否加载的部分
export const asyncRouterMap = [ export const asyncRouterMap = [
{ {
// 定义一个商品管理模块pms相关的路由配置路径为 '/pms'对应的组件是之前导入的Layout组件作为布局框架
path: '/pms', path: '/pms',
component: Layout, component: Layout,
// 设置重定向路径为 '/pms/product',访问 '/pms' 时会自动跳转到商品列表页面相关的子路由
redirect: '/pms/product', redirect: '/pms/product',
// 给这个路由命名为 'pms',方便后续操作识别
name: 'pms', name: 'pms',
// 在路由的元数据中设置标题为 '商品',图标为 'product',用于侧边栏菜单等地方显示
meta: {title: '商品', icon: 'product'}, meta: {title: '商品', icon: 'product'},
children: [{ // 定义该路由下的子路由数组,包含商品管理相关的各个具体功能页面的路由配置
path: 'product', children: [
name: 'product',
component: () => import('@/views/pms/product/index'),
meta: {title: '商品列表', icon: 'product-list'}
},
{ {
// 商品列表页面的子路由,路径为 'product',命名为 'product',通过动态导入加载对应的组件,用于展示商品列表信息
path: 'product',
name: 'product',
component: () => import('@/views/pms/product/index'),
meta: {title: '商品列表', icon: 'product-list'}
},
{
// 添加商品页面的子路由,命名为 'addProduct',通过动态导入加载对应组件,用于添加商品的操作页面
path: 'addProduct', path: 'addProduct',
name: 'addProduct', name: 'addProduct',
component: () => import('@/views/pms/product/add'), component: () => import('@/views/pms/product/add'),
meta: {title: '添加商品', icon: 'product-add'} meta: {title: '添加商品', icon: 'product-add'}
}, },
{ {
// 修改商品页面的子路由,命名为 'updateProduct',通过动态导入加载对应组件,设置 `hidden` 属性为 `true`,意味着这个页面不会显示在侧边栏等常规菜单中,可能是通过其他方式(比如在商品列表页点击编辑按钮进入)访问
path: 'updateProduct', path: 'updateProduct',
name: 'updateProduct', name: 'updateProduct',
component: () => import('@/views/pms/product/update'), component: () => import('@/views/pms/product/update'),
@ -73,12 +115,14 @@ export const asyncRouterMap = [
hidden: true hidden: true
}, },
{ {
// 商品分类页面的子路由,命名为 'productCate',通过动态导入加载对应组件,用于商品分类管理相关操作
path: 'productCate', path: 'productCate',
name: 'productCate', name: 'productCate',
component: () => import('@/views/pms/productCate/index'), component: () => import('@/views/pms/productCate/index'),
meta: {title: '商品分类', icon: 'product-cate'} meta: {title: '商品分类', icon: 'product-cate'}
}, },
{ {
// 添加商品分类页面的子路由,命名为 'addProductCate',通过动态导入加载对应组件,设置 `hidden` 属性为 `true`,不会在常规菜单显示,可能是特定操作下进入的页面
path: 'addProductCate', path: 'addProductCate',
name: 'addProductCate', name: 'addProductCate',
component: () => import('@/views/pms/productCate/add'), component: () => import('@/views/pms/productCate/add'),
@ -86,6 +130,7 @@ export const asyncRouterMap = [
hidden: true hidden: true
}, },
{ {
// 修改商品分类页面的子路由,命名为 'updateProductCate',通过动态导入加载对应组件,设置 `hidden` 属性为 `true`,同样不在常规菜单展示,用于特定的修改操作入口
path: 'updateProductCate', path: 'updateProductCate',
name: 'updateProductCate', name: 'updateProductCate',
component: () => import('@/views/pms/productCate/update'), component: () => import('@/views/pms/productCate/update'),
@ -93,12 +138,14 @@ export const asyncRouterMap = [
hidden: true hidden: true
}, },
{ {
// 商品类型页面的子路由,命名为 'productAttr',通过动态导入加载对应组件,用于管理商品类型相关功能
path: 'productAttr', path: 'productAttr',
name: 'productAttr', name: 'productAttr',
component: () => import('@/views/pms/productAttr/index'), component: () => import('@/views/pms/productAttr/index'),
meta: {title: '商品类型', icon: 'product-attr'} meta: {title: '商品类型', icon: 'product-attr'}
}, },
{ {
// 商品属性列表页面的子路由,命名为 'productAttrList',通过动态导入加载对应组件,设置 `hidden` 属性为 `true`,不常规显示,可能是特定的查看属性列表的入口
path: 'productAttrList', path: 'productAttrList',
name: 'productAttrList', name: 'productAttrList',
component: () => import('@/views/pms/productAttr/productAttrList'), component: () => import('@/views/pms/productAttr/productAttrList'),
@ -106,6 +153,7 @@ export const asyncRouterMap = [
hidden: true hidden: true
}, },
{ {
// 添加商品属性页面的子路由,命名为 'addProductAttr',通过动态导入加载对应组件,设置 `hidden` 属性为 `true`,不是常规菜单可见的页面,用于添加商品属性操作入口
path: 'addProductAttr', path: 'addProductAttr',
name: 'addProductAttr', name: 'addProductAttr',
component: () => import('@/views/pms/productAttr/addProductAttr'), component: () => import('@/views/pms/productAttr/addProductAttr'),
@ -113,6 +161,7 @@ export const asyncRouterMap = [
hidden: true hidden: true
}, },
{ {
// 修改商品属性页面的子路由,命名为 'updateProductAttr',通过动态导入加载对应组件,设置 `hidden` 属性为 `true`,不在常规菜单展示,用于修改商品属性的操作入口
path: 'updateProductAttr', path: 'updateProductAttr',
name: 'updateProductAttr', name: 'updateProductAttr',
component: () => import('@/views/pms/productAttr/updateProductAttr'), component: () => import('@/views/pms/productAttr/updateProductAttr'),
@ -120,12 +169,14 @@ export const asyncRouterMap = [
hidden: true hidden: true
}, },
{ {
// 品牌管理页面的子路由,命名为 'brand',通过动态导入加载对应组件,用于品牌管理相关操作,比如查看、编辑品牌等
path: 'brand', path: 'brand',
name: 'brand', name: 'brand',
component: () => import('@/views/pms/brand/index'), component: () => import('@/views/pms/brand/index'),
meta: {title: '品牌管理', icon: 'product-brand'} meta: {title: '品牌管理', icon: 'product-brand'}
}, },
{ {
// 添加品牌页面的子路由,命名为 'addBrand',通过动态导入加载对应组件,设置 `hidden` 属性为 `true`,不是常规显示的页面,用于添加品牌的操作入口
path: 'addBrand', path: 'addBrand',
name: 'addBrand', name: 'addBrand',
component: () => import('@/views/pms/brand/add'), component: () => import('@/views/pms/brand/add'),
@ -133,6 +184,7 @@ export const asyncRouterMap = [
hidden: true hidden: true
}, },
{ {
// 编辑品牌页面的子路由,命名为 'updateBrand',通过动态导入加载对应组件,设置 `hidden` 属性为 `true`,不在常规菜单出现,用于编辑品牌的操作入口
path: 'updateBrand', path: 'updateBrand',
name: 'updateBrand', name: 'updateBrand',
component: () => import('@/views/pms/brand/update'), component: () => import('@/views/pms/brand/update'),
@ -142,6 +194,8 @@ export const asyncRouterMap = [
] ]
}, },
{ {
// 订单管理模块oms相关的路由配置路径为 '/oms'对应Layout组件作为布局框架以下类似的结构都是对订单管理各功能页面路由的详细配置
path: '/oms', path: '/oms',
component: Layout, component: Layout,
redirect: '/oms/order', redirect: '/oms/order',

@ -1,57 +1,84 @@
// id "app"
#app { #app {
// // "main-container"
.main-container { .main-container {
// 100%
min-height: 100%; min-height: 100%;
transition: margin-left .28s; // margin-left 0.28
transition: margin-left.28s;
// 180px使
margin-left: 180px; margin-left: 180px;
} }
// // "sidebar-container"
.sidebar-container { .sidebar-container {
// "horizontal-collapse-transition"
.horizontal-collapse-transition { .horizontal-collapse-transition {
// 0 widthpadding-leftpadding-right使 ease-in-out 0
transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
} }
transition: width .28s; // 0.28
width: 180px !important; transition: width.28s;
// 180px"!important" 使
width: 180px!important;
// 100%使
height: 100%; height: 100%;
// fixed使
position: fixed; position: fixed;
// 0px访
font-size: 0px; font-size: 0px;
// 0
top: 0; top: 0;
// 0 height: 100% 使
bottom: 0; bottom: 0;
// 0使
left: 0; left: 0;
// z-index 1001
z-index: 1001; z-index: 1001;
//
overflow: hidden; overflow: hidden;
// <a> 使 100%
a { a {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
} }
// "svg-icon" SVG 16px
.svg-icon { .svg-icon {
margin-right: 16px; margin-right: 16px;
} }
// "el-menu" 使 Element UI border: none 100%
.el-menu { .el-menu {
border: none; border: none;
width: 100% !important; width: 100%!important;
} }
} }
// "hideSidebar"
.hideSidebar { .hideSidebar {
// "hideSidebar" "sidebar-container" 36px
.sidebar-container { .sidebar-container {
width: 36px !important; width: 36px!important;
} }
// "hideSidebar" "main-container" 36px使
.main-container { .main-container {
margin-left: 36px; margin-left: 36px;
} }
// "submenu-title-noDropdown" 10px"!important" position: relative便
.submenu-title-noDropdown { .submenu-title-noDropdown {
padding-left: 10px !important; padding-left: 10px!important;
position: relative; position: relative;
// "el-tooltip" 0 10px
.el-tooltip { .el-tooltip {
padding: 0 10px !important; padding: 0 10px!important;
} }
} }
// "el-submenu"
.el-submenu { .el-submenu {
// "el-submenu" "el-submenu__title" 10px
&>.el-submenu__title { &>.el-submenu__title {
padding-left: 10px !important; padding-left: 10px!important;
// "el-submenu__title" <span> 0 overflow: hidden visibility: hidden
&>span { &>span {
height: 0; height: 0;
width: 0; width: 0;
@ -59,6 +86,7 @@
visibility: hidden; visibility: hidden;
display: inline-block; display: inline-block;
} }
// "el-submenu__title" "el-submenu__icon-arrow" display: none
.el-submenu__icon-arrow { .el-submenu__icon-arrow {
display: none; display: none;
} }
@ -66,28 +94,34 @@
} }
} }
.sidebar-container .nest-menu .el-submenu>.el-submenu__title, // "sidebar-container" "nest-menu" "el-submenu" "el-submenu__title" "sidebar-container" "el-menu-item" 180px $subMenuBg $subMenuBg SCSS
.sidebar-container .el-submenu .el-menu-item { .sidebar-container.nest-menu.el-submenu>.el-submenu__title,
min-width: 180px !important; .sidebar-container.el-submenu.el-menu-item {
background-color: $subMenuBg !important; min-width: 180px!important;
background-color: $subMenuBg!important;
// $menuHover SCSS
&:hover { &:hover {
background-color: $menuHover !important; background-color: $menuHover!important;
} }
} }
.el-menu--collapse .el-menu .el-submenu { // "el-menu--collapse" "el-menu" "el-submenu" 180px
min-width: 180px !important; .el-menu--collapse.el-menu.el-submenu {
min-width: 180px!important;
} }
// // "mobile"
.mobile { .mobile {
// "main-container" 0px使
.main-container { .main-container {
margin-left: 0px; margin-left: 0px;
} }
// "sidebar-container" 50px 0.28 180px
.sidebar-container { .sidebar-container {
top: 50px; top: 50px;
transition: transform .28s; transition: transform.28s;
width: 180px !important; width: 180px!important;
} }
// "mobile" "hideSidebar" "sidebar-container" 0.3s transform 180px使 translate3d 3D X
&.hideSidebar { &.hideSidebar {
.sidebar-container { .sidebar-container {
transition-duration: 0.3s; transition-duration: 0.3s;
@ -96,7 +130,9 @@
} }
} }
// "withoutAnimation"
.withoutAnimation { .withoutAnimation {
// "withoutAnimation" "main-container" "sidebar-container" none使
.main-container, .main-container,
.sidebar-container { .sidebar-container {
transition: none; transition: none;

@ -1,42 +1,69 @@
// date.js // date.js
// 这个函数用于将一个日期对象按照指定的格式进行格式化
export function formatDate(date, fmt) { export function formatDate(date, fmt) {
// 如果格式化字符串中包含'y+'(表示年份)
if (/(y+)/.test(fmt)) { if (/(y+)/.test(fmt)) {
// 使用正则表达式匹配到的'y+'替换为对应的年份值
// 获取当前日期的年份,将其转换为字符串
// 根据'y+'的长度,从年份字符串的末尾截取相应长度的字符进行替换
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)); fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
} }
// 创建一个对象,包含了从日期对象中获取各种时间单位的方法
let o = { let o = {
'M+': date.getMonth() + 1, 'M+': date.getMonth() + 1, // 获取月份月份是从0开始的所以要加1
'd+': date.getDate(), 'd+': date.getDate(), // 获取日期
'h+': date.getHours(), 'h+': date.getHours(), // 获取小时
'm+': date.getMinutes(), 'm+': date.getMinutes(), // 获取分钟
's+': date.getSeconds() 's+': date.getSeconds() // 获取秒
}; };
// 遍历对象o中的每个属性时间单位
for (let k in o) { for (let k in o) {
// 使用正则表达式检查格式化字符串中是否包含当前时间单位的格式
if (new RegExp(`(${k})`).test(fmt)) { if (new RegExp(`(${k})`).test(fmt)) {
// 将当前时间单位的值转换为字符串
let str = o[k] + ''; let str = o[k] + '';
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str)); // 如果当前时间单位在格式化字符串中的格式长度为1则直接替换
// 如果长度大于1则调用padLeftZero函数进行补零操作后再替换
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1)? str : padLeftZero(str));
} }
} }
// 返回格式化后的字符串
return fmt; return fmt;
} }
function padLeftZero(str) { // 这个函数用于将字符串格式的日期转换为日期对象
return ('00' + str).substr(str.length); // 如果没有传入分隔符参数,则默认使用'-'作为分隔符
}
export function str2Date(dateStr, separator) { export function str2Date(dateStr, separator) {
if (!separator) { if (!separator) {
separator = "-"; separator = "-";
} }
// 根据分隔符将日期字符串拆分成数组
let dateArr = dateStr.split(separator); let dateArr = dateStr.split(separator);
// 解析年份
let year = parseInt(dateArr[0]); let year = parseInt(dateArr[0]);
// 解析月份
let month; let month;
//处理月份为04这样的情况 // 如果月份字符串以'0'开头(例如'04'),则去掉'0'后再解析为整数
if (dateArr[1].indexOf("0") == 0) { if (dateArr[1].indexOf("0") == 0) {
month = parseInt(dateArr[1].substring(1)); month = parseInt(dateArr[1].substring(1));
} else { } else {
month = parseInt(dateArr[1]); month = parseInt(dateArr[1]);
} }
// 解析日期
let day = parseInt(dateArr[2]); let day = parseInt(dateArr[2]);
// 使用解析后的年、月、日创建一个新的日期对象
// 注意月份在日期对象中是从0开始计数的所以要减1
let date = new Date(year, month - 1, day); let date = new Date(year, month - 1, day);
// 返回日期对象
return date; return date;
} }
// 这个函数用于在字符串左边补零
// 例如,如果传入'5',则返回'05'
function padLeftZero(str) {
// 在字符串前面加上'00',然后根据原字符串的长度从右边截取
return ('00' + str).substr(str.length);
}

@ -1,54 +1,78 @@
// 这个函数用于将给定的时间数据按照指定的格式进行格式化处理支持传入不同类型的时间表示形式如时间戳、Date对象等并转化为格式化后的字符串
export function parseTime(time, cFormat) { export function parseTime(time, cFormat) {
// 如果没有传入任何参数arguments.length === 0表示参数个数为0则直接返回null表示无法进行时间格式化操作
if (arguments.length === 0) { if (arguments.length === 0) {
return null return null
} }
// 如果没有传入自定义的格式化字符串cFormat则使用默认的格式化字符串这里定义了一个包含年、月、日、时、分、秒占位符的格式模板
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date let date
// 判断传入的时间参数是否已经是一个Date对象如果是则直接将其赋值给date变量后续用于提取时间信息进行格式化
if (typeof time === 'object') { if (typeof time === 'object') {
date = time date = time
} else { } else {
// 如果传入的时间参数不是Date对象先进行以下处理
// 如果传入的时间字符串长度为10位通常表示以秒为单位的时间戳比如从后端获取的时间戳可能是这种形式则将其转换为以毫秒为单位的时间戳乘以1000因为JavaScript中Date对象的构造函数接收的是以毫秒为单位的时间戳
if (('' + time).length === 10) time = parseInt(time) * 1000 if (('' + time).length === 10) time = parseInt(time) * 1000
// 使用转换后的时间戳或者本身就是以毫秒为单位的时间戳创建一个Date对象用于后续获取年、月、日等时间信息
date = new Date(time) date = new Date(time)
} }
// 创建一个对象用于存储从Date对象中提取出的各个时间单位对应的数值键名与格式化字符串中的占位符相对应
const formatObj = { const formatObj = {
y: date.getFullYear(), y: date.getFullYear(), // 获取年份例如2024
m: date.getMonth() + 1, m: date.getMonth() + 1, // 获取月份JavaScript中月份是从0开始计数的所以要加1范围是1 - 12
d: date.getDate(), d: date.getDate(), // 获取日期即一个月中的第几天范围是1 - 31
h: date.getHours(), h: date.getHours(), // 获取小时范围是0 - 23
i: date.getMinutes(), i: date.getMinutes(), // 获取分钟范围是0 - 59
s: date.getSeconds(), s: date.getSeconds(), // 获取秒范围是0 - 59
a: date.getDay() a: date.getDay() // 获取星期几返回值是0星期日 - 6星期六
} }
// 使用正则表达式替换格式化字符串中的占位符,将其替换为实际的时间数值对应的字符串表示形式
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key] let value = formatObj[key]
// 如果占位符是 'a'表示星期几则将数字形式的星期几转换为中文汉字形式例如1转换为“一”表示星期一
if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1] if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
// 如果占位符对应的时间数值小于10并且占位符长度大于0防止出现类似 {0} 这种不需要补0的情况则在数值前面补0使其格式更规范例如1变为01
if (result.length > 0 && value < 10) { if (result.length > 0 && value < 10) {
value = '0' + value value = '0' + value
} }
// 返回处理后的时间数值对应的字符串如果数值为0或者不存在比如没有获取到对应的时间信息则返回0
return value || 0 return value || 0
}) })
// 返回最终格式化后的时间字符串
return time_str return time_str
} }
// 这个函数用于根据时间与当前时间的时间差以相对友好的格式来展示时间例如“刚刚”、“10分钟前”等如果时间差较大或者传入了格式化选项则按照指定格式进行格式化展示
export function formatTime(time, option) { export function formatTime(time, option) {
// 将传入的时间参数转换为以毫秒为单位的时间戳假设传入的是以秒为单位的时间戳乘以1000进行转换方便后续进行时间差的计算等操作
time = +time * 1000 time = +time * 1000
// 根据转换后的时间戳创建一个Date对象用于获取时间信息
const d = new Date(time) const d = new Date(time)
// 获取当前时间的时间戳(以毫秒为单位)
const now = Date.now() const now = Date.now()
// 计算当前时间与给定时间的时间差单位为秒通过将时间戳差值除以1000得到
const diff = (now - d) / 1000 const diff = (now - d) / 1000
// 如果时间差小于30秒直接返回“刚刚”表示时间很近
if (diff < 30) { if (diff < 30) {
return '刚刚' return '刚刚'
} else if (diff < 3600) { // less 1 hour } else if (diff < 3600) { // 如果时间差小于1小时1小时等于3600秒
// 计算时间差对应的分钟数向上取整并返回类似“10分钟前”的格式
return Math.ceil(diff / 60) + '分钟前' return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) { } else if (diff < 3600 * 24) { // 如果时间差小于1天1天等于3600 * 24秒
// 计算时间差对应的小时数向上取整并返回类似“3小时前”的格式
return Math.ceil(diff / 3600) + '小时前' return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) { } else if (diff < 3600 * 24 * 2) { // 如果时间差小于2天
// 直接返回“1天前”
return '1天前' return '1天前'
} }
// 如果传入了格式化选项option则调用parseTime函数按照传入的选项格式来格式化时间并返回
if (option) { if (option) {
return parseTime(time, option) return parseTime(time, option)
} else { } else {
// 如果没有传入格式化选项则以“月日时分”的格式返回时间例如“10月15日12时30分”
return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
} }
} }

@ -1,15 +1,33 @@
<template> <template>
<div> <div>
<!-- 页面主体容器 -->
<div class="app-container"> <div class="app-container">
<!-- 左侧图片展示 -->
<el-col :span="12"> <el-col :span="12">
<!-- 显示 404 错误图片 -->
<img :src="img_404" alt="404" class="img-style"> <img :src="img_404" alt="404" class="img-style">
</el-col> </el-col>
<!-- 右侧提示信息与按钮 -->
<el-col :span="12"> <el-col :span="12">
<div style="margin-left: 100px;margin-top: 60px"> <div style="margin-left: 100px; margin-top: 60px">
<!-- 错误标题 -->
<h1 class="color-main">OOPS!</h1> <h1 class="color-main">OOPS!</h1>
<!-- 错误描述 -->
<h2 style="color: #606266">很抱歉页面它不小心迷路了</h2> <h2 style="color: #606266">很抱歉页面它不小心迷路了</h2>
<div style="color:#909399;font-size: 14px">请检查您输入的网址是否正确请点击以下按钮返回主页或者发送错误报告</div> <!-- 进一步的操作提示 -->
<el-button style="margin-top: 20px" type="primary" round @click="handleGoMain"></el-button> <div style="color:#909399; font-size: 14px">
请检查您输入的网址是否正确请点击以下按钮返回主页或者发送错误报告
</div>
<!-- 返回首页按钮 -->
<el-button
style="margin-top: 20px"
type="primary"
round
@click="handleGoMain"
>
返回首页
</el-button>
</div> </div>
</el-col> </el-col>
</div> </div>
@ -17,33 +35,50 @@
</template> </template>
<script> <script>
// 404
import img_404 from '@/assets/images/gif_404.gif'; import img_404 from '@/assets/images/gif_404.gif';
export default { export default {
name: 'wrongPage', name: 'wrongPage', //
data() { data() {
return { return {
// 404
img_404 img_404
} };
}, },
methods: { methods: {
/**
* 返回首页的处理函数
* 将用户重定向到主页
*/
handleGoMain() { handleGoMain() {
this.$router.push({path: '/'}) this.$router.push({ path: '/' });
} }
}, },
} };
</script> </script>
<style scoped> <style scoped>
/* 页面主容器样式 */
.app-container { .app-container {
width: 80%; width: 80%;
margin: 120px auto; margin: 120px auto; /* 页面居中,距离顶部 120px */
display: flex; /* 使用 flex 布局 */
align-items: center; /* 垂直居中 */
} }
/* 404 图片样式 */
.img-style { .img-style {
width: auto; width: auto; /* 宽度自适应 */
height: auto; height: auto; /* 高度自适应 */
max-width: 100%; max-width: 100%; /* 图片最大宽度不超过容器 */
max-height: 100%; max-height: 100%; /* 图片最大高度不超过容器 */
}
/* 标题主颜色样式 */
.color-main {
color: #409EFF; /* 标题颜色 */
font-size: 48px; /* 字体大小 */
font-weight: bold; /* 加粗字体 */
} }
</style> </style>

@ -1,10 +1,17 @@
<template> <template>
<!-- 整体的应用容器用于包裹页面内的各个功能模块通过设置外边距来控制在页面中的位置 -->
<div class="app-container"> <div class="app-container">
<!-- 用于展示一些地址相关链接信息的布局容器 -->
<div class="address-layout"> <div class="address-layout">
<!-- el-row 组件用于创建行布局通过 :gutter 属性设置列之间的间隔为 20px -->
<el-row :gutter="20"> <el-row :gutter="20">
<!-- el-col 组件用于定义列这里设置占 6 列的宽度在使用了 12 列布局系统的情况下 -->
<el-col :span="6"> <el-col :span="6">
<!-- 带有外边框样式的容器 -->
<div class="out-border"> <div class="out-border">
<!-- 标题部分用于显示该区域的标题 -->
<div class="layout-title">学习教程</div> <div class="layout-title">学习教程</div>
<!-- 主要内容部分展示具体的链接文本链接指向外部网站点击会在新标签页打开 -->
<div class="color-main address-content"> <div class="color-main address-content">
<a href="https://www.macrozheng.com" target="_blank">mall学习教程</a> <a href="https://www.macrozheng.com" target="_blank">mall学习教程</a>
</div> </div>
@ -28,12 +35,17 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<!-- 用于展示一些统计总数相关信息的布局容器 -->
<div class="total-layout"> <div class="total-layout">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6"> <el-col :span="6">
<!-- 用于包裹单个统计项的框架容器 -->
<div class="total-frame"> <div class="total-frame">
<!-- 通过绑定属性显示对应的图标图标样式通过类名控制 -->
<img :src="img_home_order" class="total-icon"> <img :src="img_home_order" class="total-icon">
<!-- 统计项的标题部分 -->
<div class="total-title">今日订单总数</div> <div class="total-title">今日订单总数</div>
<!-- 显示具体统计数值的部分 -->
<div class="total-value">200</div> <div class="total-value">200</div>
</div> </div>
</el-col> </el-col>
@ -51,30 +63,40 @@
<div class="total-value">5000.00</div> <div class="total-value">5000.00</div>
</div> </div>
</el-col> </el-col>
<!-- 以下是被注释掉的一个统计项可能原本用于展示近 7 天销售总额相关信息 -->
<!--<el-col :span="6">--> <!--<el-col :span="6">-->
<!--<div class="total-frame">--> <!--<div class="total-frame">-->
<!--<svg-icon icon-class="total-week" class="total-icon">--> <!--<svg-icon icon-class="total-week" class="total-icon">-->
<!--</svg-icon>--> <!--</svg-icon>-->
<!--<div class="total-title">近7天销售总额</div>--> <!--<div class="total-title">近7天销售总额</div>-->
<!--<div class="total-value">50000.00</div>--> <!--<div class="total-value">50000.00</div>-->
<!--</div>--> <!--</div>-->
<!--</el-col>--> <!--</el-col>-->
</el-row> </el-row>
</div> </div>
<!-- 用于展示一些特定信息如二维码提示文字等的卡片式布局容器 -->
<el-card class="mine-layout"> <el-card class="mine-layout">
<!-- 将图片在水平方向上居中显示图片展示的是一个二维码设置了宽度和高度 -->
<div style="text-align: center"> <div style="text-align: center">
<img width="150px" height="150px" src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg"> <img width="150px" height="150px" src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg">
</div> </div>
<!-- 提示文字告知用户 mall 全套学习教程正在连载中 -->
<div style="text-align: center">mall全套学习教程连载中</div> <div style="text-align: center">mall全套学习教程连载中</div>
<!-- 另一行提示文字引导用户关注公众号以第一时间获取相关内容 -->
<div style="text-align: center;margin-top: 5px"><span class="color-main">关注公号</span>第一时间获取</div> <div style="text-align: center;margin-top: 5px"><span class="color-main">关注公号</span>第一时间获取</div>
</el-card> </el-card>
<!-- 用于展示待处理事务相关信息的布局容器 -->
<div class="un-handle-layout"> <div class="un-handle-layout">
<!-- 标题部分用于标识这是待处理事务区域 -->
<div class="layout-title">待处理事务</div> <div class="layout-title">待处理事务</div>
<!-- 内容部分包含多个行和列来展示不同类型的待处理事务信息 -->
<div class="un-handle-content"> <div class="un-handle-content">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="8"> <el-col :span="8">
<div class="un-handle-item"> <div class="un-handle-item">
<!-- 显示事务类型的文字设置了字体粗细 -->
<span class="font-medium">待付款订单</span> <span class="font-medium">待付款订单</span>
<!-- 显示对应事务数量的文字通过浮动使其靠右显示并设置了危险红色的颜色样式 -->
<span style="float: right" class="color-danger">(10)</span> <span style="float: right" class="color-danger">(10)</span>
</div> </div>
</el-col> </el-col>
@ -133,6 +155,7 @@
</el-row> </el-row>
</div> </div>
</div> </div>
<!-- 用于展示商品和用户总览信息的布局容器 -->
<div class="overview-layout"> <div class="overview-layout">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="12"> <el-col :span="12">
@ -140,12 +163,14 @@
<div class="layout-title">商品总览</div> <div class="layout-title">商品总览</div>
<div style="padding: 40px"> <div style="padding: 40px">
<el-row> <el-row>
<!-- 展示商品相关统计数值的列设置了危险红色的颜色样式 -->
<el-col :span="6" class="color-danger overview-item-value">100</el-col> <el-col :span="6" class="color-danger overview-item-value">100</el-col>
<el-col :span="6" class="color-danger overview-item-value">400</el-col> <el-col :span="6" class="color-danger overview-item-value">400</el-col>
<el-col :span="6" class="color-danger overview-item-value">50</el-col> <el-col :span="6" class="color-danger overview-item-value">50</el-col>
<el-col :span="6" class="color-danger overview-item-value">500</el-col> <el-col :span="6" class="color-danger overview-item-value">500</el-col>
</el-row> </el-row>
<el-row class="font-medium"> <el-row class="font-medium">
<!-- 展示对应统计数值含义的标题列 -->
<el-col :span="6" class="overview-item-title">已下架</el-col> <el-col :span="6" class="overview-item-title">已下架</el-col>
<el-col :span="6" class="overview-item-title">已上架</el-col> <el-col :span="6" class="overview-item-title">已上架</el-col>
<el-col :span="6" class="overview-item-title">库存紧张</el-col> <el-col :span="6" class="overview-item-title">库存紧张</el-col>
@ -175,12 +200,14 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<!-- 用于展示订单统计相关信息的布局容器 -->
<div class="statistics-layout"> <div class="statistics-layout">
<div class="layout-title">订单统计</div> <div class="layout-title">订单统计</div>
<el-row> <el-row>
<el-col :span="4"> <el-col :span="4">
<div style="padding: 20px"> <div style="padding: 20px">
<div> <div>
<!-- 显示本月订单总数相关信息包括描述文字具体数值以及同比上月的增长情况 -->
<div style="color: #909399;font-size: 14px">本月订单总数</div> <div style="color: #909399;font-size: 14px">本月订单总数</div>
<div style="color: #606266;font-size: 24px;padding: 10px 0">10000</div> <div style="color: #606266;font-size: 24px;padding: 10px 0">10000</div>
<div> <div>
@ -189,6 +216,7 @@
</div> </div>
</div> </div>
<div style="margin-top: 20px;"> <div style="margin-top: 20px;">
<!-- 显示本周订单总数相关信息同样包括描述文字数值和同比上周的变化情况 -->
<div style="color: #909399;font-size: 14px">本周订单总数</div> <div style="color: #909399;font-size: 14px">本周订单总数</div>
<div style="color: #606266;font-size: 24px;padding: 10px 0">1000</div> <div style="color: #606266;font-size: 24px;padding: 10px 0">1000</div>
<div> <div>
@ -197,6 +225,7 @@
</div> </div>
</div> </div>
<div style="margin-top: 20px;"> <div style="margin-top: 20px;">
<!-- 显示本月销售总额相关信息 -->
<div style="color: #909399;font-size: 14px">本月销售总额</div> <div style="color: #909399;font-size: 14px">本月销售总额</div>
<div style="color: #606266;font-size: 24px;padding: 10px 0">100000</div> <div style="color: #606266;font-size: 24px;padding: 10px 0">100000</div>
<div> <div>
@ -205,6 +234,7 @@
</div> </div>
</div> </div>
<div style="margin-top: 20px;"> <div style="margin-top: 20px;">
<!-- 显示本周销售总额相关信息 -->
<div style="color: #909399;font-size: 14px">本周销售总额</div> <div style="color: #909399;font-size: 14px">本周销售总额</div>
<div style="color: #606266;font-size: 24px;padding: 10px 0">50000</div> <div style="color: #606266;font-size: 24px;padding: 10px 0">50000</div>
<div> <div>
@ -216,6 +246,7 @@
</el-col> </el-col>
<el-col :span="20"> <el-col :span="20">
<div style="padding: 10px;border-left:1px solid #DCDFE6"> <div style="padding: 10px;border-left:1px solid #DCDFE6">
<!-- el-date-picker 组件用于选择日期范围以下是对其各种属性的设置说明 -->
<el-date-picker <el-date-picker
style="float: right;z-index: 1" style="float: right;z-index: 1"
size="small" size="small"
@ -230,6 +261,7 @@
:picker-options="pickerOptions"> :picker-options="pickerOptions">
</el-date-picker> </el-date-picker>
<div> <div>
<!-- ve-line 组件可能用于展示图表比如折线图等通过绑定多个属性来设置图表的数据是否显示图例加载状态等 -->
<ve-line <ve-line
:data="chartData" :data="chartData"
:legend-visible="false" :legend-visible="false"
@ -243,7 +275,6 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import {str2Date} from '@/utils/date'; import {str2Date} from '@/utils/date';
import img_home_order from '@/assets/images/home_order.png'; import img_home_order from '@/assets/images/home_order.png';

@ -1,36 +1,53 @@
<template> <template>
<!-- 最外层的 div 作为整个应用布局的包裹容器通过绑定 :class 属性动态地根据计算属性 classObj 的值来添加相应的类名 -->
<div class="app-wrapper" :class="classObj"> <div class="app-wrapper" :class="classObj">
<!-- 引入自定义的 Sidebar 组件用于展示侧边栏内容给它添加了一个类名 "sidebar-container"方便后续进行样式设置 -->
<sidebar class="sidebar-container"></sidebar> <sidebar class="sidebar-container"></sidebar>
<!-- 这是主要内容的容器 div用于放置页面中除侧边栏之外的主体部分 -->
<div class="main-container"> <div class="main-container">
<!-- 引入自定义的 Navbar 组件用于展示页面顶部的导航栏 -->
<navbar></navbar> <navbar></navbar>
<!-- 引入自定义的 AppMain 组件用于展示页面主体的核心内容 -->
<app-main></app-main> <app-main></app-main>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
// components NavbarSidebar AppMain 便使
import { Navbar, Sidebar, AppMain } from './components' import { Navbar, Sidebar, AppMain } from './components'
// mixin ResizeHandler ResizeMixinmixin
import ResizeMixin from './mixin/ResizeHandler' import ResizeMixin from './mixin/ResizeHandler'
export default { export default {
name: 'layout', name: 'layout',
// components 使 NavbarSidebar AppMain 使
components: { components: {
Navbar, Navbar,
Sidebar, Sidebar,
AppMain AppMain
}, },
// 使 mixins ResizeMixinmixin layout
mixins: [ResizeMixin], mixins: [ResizeMixin],
computed: { computed: {
// sidebar Vuex store app sidebar
// 使 sidebar
sidebar() { sidebar() {
return this.$store.state.app.sidebar return this.$store.state.app.sidebar
}, },
// device Vuex app device 'mobile'
device() { device() {
return this.$store.state.app.device return this.$store.state.app.device
}, },
// classObj sidebar device
//
classObj() { classObj() {
return { return {
hideSidebar: !this.sidebar.opened, // !this.sidebar.opened hideSidebar
hideSidebar:!this.sidebar.opened,
// withoutAnimation true withoutAnimation
withoutAnimation: this.sidebar.withoutAnimation, withoutAnimation: this.sidebar.withoutAnimation,
// 'mobile' this.device ==='mobile' mobile 便
mobile: this.device === 'mobile' mobile: this.device === 'mobile'
} }
} }
@ -38,12 +55,44 @@ export default {
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <script>
@import "src/styles/mixin.scss"; // components NavbarSidebar AppMain 便使
.app-wrapper { import { Navbar, Sidebar, AppMain } from './components'
@include clearfix; // mixin ResizeHandler ResizeMixinmixin
position: relative; import ResizeMixin from './mixin/ResizeHandler'
height: 100%;
width: 100%; export default {
name: 'layout',
// components 使 NavbarSidebar AppMain 使
components: {
Navbar,
Sidebar,
AppMain
},
// 使 mixins ResizeMixinmixin layout
mixins: [ResizeMixin],
computed: {
// sidebar Vuex store app sidebar
// 使 sidebar
sidebar() {
return this.$store.state.app.sidebar
},
// device Vuex app device 'mobile'
device() {
return this.$store.state.app.device
},
// classObj sidebar device
//
classObj() {
return {
// !this.sidebar.opened hideSidebar
hideSidebar:!this.sidebar.opened,
// withoutAnimation true withoutAnimation
withoutAnimation: this.sidebar.withoutAnimation,
// 'mobile' this.device ==='mobile' mobile 便
mobile: this.device === 'mobile'
}
}
} }
</style> }
</script>

@ -1,18 +1,28 @@
<template> <template>
<!-- el-menu 组件用于创建一个菜单这里设置了类名为 "navbar"模式为水平方向horizontal通常用于页面顶部的导航栏布局 -->
<el-menu class="navbar" mode="horizontal"> <el-menu class="navbar" mode="horizontal">
<!-- Hamburger 组件用于实现类似汉堡包样式的图标按钮点击可切换侧边栏状态通过绑定 :toggleClick 事件来触发切换侧边栏的方法:isActive 根据侧边栏的打开状态来显示相应的样式 -->
<hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger> <hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<!-- 引入 Breadcrumb 组件可能用于展示页面的面包屑导航显示当前页面在整个网站结构中的层级路径但此处没有传入具体属性 -->
<breadcrumb></breadcrumb> <breadcrumb></breadcrumb>
<!-- el-dropdown 组件用于创建一个下拉菜单触发方式设置为点击click这里用于展示用户相关操作的下拉菜单比如首页退出等功能 -->
<el-dropdown class="avatar-container" trigger="click"> <el-dropdown class="avatar-container" trigger="click">
<!-- 内部的 div 作为一个容器包裹用户头像和下拉箭头图标 -->
<div class="avatar-wrapper"> <div class="avatar-wrapper">
<!-- 通过绑定 :src 属性展示用户头像图片图片的源地址由变量 "avatar" 决定 -->
<img class="user-avatar" :src="avatar"> <img class="user-avatar" :src="avatar">
<!-- 使用一个向下的图标el-icon-caret-bottom作为下拉菜单的指示箭头 -->
<i class="el-icon-caret-bottom"></i> <i class="el-icon-caret-bottom"></i>
</div> </div>
<!-- el-dropdown-menu 组件用于定义下拉菜单的具体内容通过 slot="dropdown" 指定它在 el-dropdown 组件中的插槽位置 -->
<el-dropdown-menu class="user-dropdown" slot="dropdown"> <el-dropdown-menu class="user-dropdown" slot="dropdown">
<!-- router-link 组件用于创建路由链接点击可跳转到对应的路由路径这里是首页路径为 "/"样式设置为行内块元素 -->
<router-link class="inlineBlock" to="/"> <router-link class="inlineBlock" to="/">
<el-dropdown-item> <el-dropdown-item>
首页 首页
</el-dropdown-item> </el-dropdown-item>
</router-link> </router-link>
<!-- 另一个 el-dropdown-item 用于展示 "退出" 操作添加了分割线样式divided通过点击内部的 span 元素触发 logout 方法来执行退出登录操作 -->
<el-dropdown-item divided> <el-dropdown-item divided>
<span @click="logout" style="display:block;">退出</span> <span @click="logout" style="display:block;">退出</span>
</el-dropdown-item> </el-dropdown-item>
@ -20,27 +30,37 @@
</el-dropdown> </el-dropdown>
</el-menu> </el-menu>
</template> </template>
<script> <script>
// vuex mapGetters Vuex store getter
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
// Breadcrumb
import Breadcrumb from '@/components/Breadcrumb' import Breadcrumb from '@/components/Breadcrumb'
// Hamburger
import Hamburger from '@/components/Hamburger' import Hamburger from '@/components/Hamburger'
export default { export default {
// 使 Breadcrumb Hamburger 便使
components: { components: {
Breadcrumb, Breadcrumb,
Hamburger Hamburger
}, },
computed: { computed: {
// 使 mapGetters Vuex store 'sidebar' 'avatar' getter
// this.sidebar this.avatar 访 store
...mapGetters([ ...mapGetters([
'sidebar', 'sidebar',
'avatar' 'avatar'
]) ])
}, },
methods: { methods: {
// toggleSideBar Vuex store ToggleSideBar action
// action Vuex
toggleSideBar() { toggleSideBar() {
this.$store.dispatch('ToggleSideBar') this.$store.dispatch('ToggleSideBar')
}, },
// logout 退
// this.$store.dispatch Vuex store LogOut action LogOut action
// Promise .then location.reload() vue-router bug
logout() { logout() {
this.$store.dispatch('LogOut').then(() => { this.$store.dispatch('LogOut').then(() => {
location.reload() // vue-router bug location.reload() // vue-router bug
@ -49,46 +69,75 @@ export default {
} }
} }
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped> <style rel="stylesheet/scss" lang="scss" scoped>
/* 为类名为 "navbar" 的元素设置样式,以下是具体的样式属性说明 */
.navbar { .navbar {
/* 设置元素的高度为 50px */
height: 50px; height: 50px;
/* 设置行高为 50px使得内部文本在垂直方向上居中对齐 */
line-height: 50px; line-height: 50px;
border-radius: 0px !important; /* 覆盖默认的边框圆角设置,将其设置为 0px使其呈现直角样式 */
border-radius: 0px!important;
/* 为类名为 "hamburger-container" 的子元素设置样式,它属于 "navbar" 元素内部的元素 */
.hamburger-container { .hamburger-container {
/* 设置行高为 58px用于垂直方向上的布局调整 */
line-height: 58px; line-height: 58px;
/* 设置高度与父元素navbar保持一致为 50px */
height: 50px; height: 50px;
/* 将元素向左浮动,使其在水平方向上靠左排列 */
float: left; float: left;
/* 设置左右内边距为 10px用于控制内部元素与边界的间距 */
padding: 0 10px; padding: 0 10px;
} }
/* 为类名为 "screenfull" 的元素设置样式,它在 "navbar" 元素内部(从样式结构看应该是与全屏相关的功能元素,但代码中未体现其完整使用场景) */
.screenfull { .screenfull {
/* 设置元素的定位为绝对定位,以便可以精确控制其在父元素中的位置 */
position: absolute; position: absolute;
/* 设置元素距离父元素右侧 90px 的位置 */
right: 90px; right: 90px;
/* 设置元素距离父元素顶部 16px 的位置 */
top: 16px; top: 16px;
/* 设置元素的颜色为红色,可能用于特定的提示等功能 */
color: red; color: red;
} }
/* 为类名为 "avatar-container" 的元素设置样式,它同样是 "navbar" 元素内部用于包裹用户头像及下拉菜单相关内容的容器 */
.avatar-container { .avatar-container {
/* 设置高度与父元素navbar一致为 50px */
height: 50px; height: 50px;
/* 将元素设置为行内块级元素,使其可以在水平方向上与其他元素并排显示,同时又能设置宽度、高度等块级元素的属性 */
display: inline-block; display: inline-block;
/* 将元素定位在父元素navbar的右侧距离右侧 35px 的位置 */
position: absolute; position: absolute;
right: 35px; right: 35px;
/* 为类名为 "avatar-wrapper" 的子元素设置样式,它是 "avatar-container" 内部进一步包裹头像和图标的容器 */
.avatar-wrapper { .avatar-wrapper {
/* 设置鼠标指针样式为手型,提示用户此处可点击操作(一般用于有交互功能的元素) */
cursor: pointer; cursor: pointer;
/* 设置元素距离顶部 5px 的外边距,用于垂直方向上的位置微调 */
margin-top: 5px; margin-top: 5px;
/* 设置元素为相对定位,以便内部的绝对定位元素可以基于它来进行定位 */
position: relative; position: relative;
/* 为类名为 "user-avatar" 的子元素(即用户头像图片元素)设置样式 */
.user-avatar { .user-avatar {
/* 设置图片的宽度为 40px */
width: 40px; width: 40px;
/* 设置图片的高度为 40px */
height: 40px; height: 40px;
/* 设置图片的边框圆角为 10px使其呈现圆角矩形的外观 */
border-radius: 10px; border-radius: 10px;
} }
/* 为类名为 "el-icon-caret-bottom" 的子元素(即下拉箭头图标元素)设置样式 */
.el-icon-caret-bottom { .el-icon-caret-bottom {
/* 设置元素为绝对定位以便精确控制其在父元素avatar-wrapper中的位置 */
position: absolute; position: absolute;
/* 设置元素距离父元素右侧 -20px 的位置(负数表示向左偏移) */
right: -20px; right: -20px;
/* 设置元素距离父元素顶部 25px 的位置,用于垂直方向上的定位 */
top: 25px; top: 25px;
/* 设置图标的字体大小为 12px调整图标大小 */
font-size: 12px; font-size: 12px;
} }
} }
} }
} }
</style> </style>

@ -1,38 +1,57 @@
// 从项目的 @/store 目录下导入 Vuex 的 store 实例,这个 store 通常用于管理应用中的状态数据以及处理相关的业务逻辑,例如控制侧边栏的显示隐藏、设备类型的切换等操作
import store from '@/store' import store from '@/store'
// 通过解构赋值从 document 对象中获取 body 属性,后续可能会基于这个 body 元素来获取页面相关的尺寸等信息,比如判断页面宽度是否满足移动端的条件
const { body } = document const { body } = document
// 定义一个常量 WIDTH表示一个宽度的阈值单位应该是像素这里设置为 1024px可能用于区分移动端和桌面端设备比如页面宽度小于这个值时认为是移动端
const WIDTH = 1024 const WIDTH = 1024
// 定义一个常量 RATIO具体含义可能和页面尺寸判断逻辑相关结合代码来看可能是在判断页面宽度是否符合移动端条件时需要减去的一个值
const RATIO = 3 const RATIO = 3
export default { export default {
// watch 选项用于监听组件中的数据变化,这里监听了 $routeVue 路由对象)的变化
watch: { watch: {
$route(route) { $route(route) {
// 判断当前设备是否为移动端(通过 this.device ==='mobile' 判断,这里的 this.device 应该是组件中的某个属性用于标识设备类型并且侧边栏是打开状态this.sidebar.opened
if (this.device === 'mobile' && this.sidebar.opened) { if (this.device === 'mobile' && this.sidebar.opened) {
// 如果满足上述条件,通过 store.dispatch 方法触发 Vuex store 中的 'CloseSideBar' 这个 action传入一个配置对象 { withoutAnimation: false },表示关闭侧边栏,并且关闭时是否有动画效果设置为 false可能在对应的 action 实现中有相应的处理逻辑来根据这个配置关闭侧边栏并控制动画展示情况)
store.dispatch('CloseSideBar', { withoutAnimation: false }) store.dispatch('CloseSideBar', { withoutAnimation: false })
} }
} }
}, },
// beforeMount 生命周期钩子函数,在组件挂载到 DOM 之前被调用。这里给 window 对象添加了一个'resize' 事件监听器,当窗口大小发生变化时,会触发 this.resizeHandler 方法,用于处理窗口大小变化相关的逻辑
beforeMount() { beforeMount() {
window.addEventListener('resize', this.resizeHandler) window.addEventListener('resize', this.resizeHandler)
}, },
// mounted 生命周期钩子函数,在组件挂载到 DOM 之后被调用。这里首先调用 this.isMobile 方法来判断当前设备是否为移动端
mounted() { mounted() {
const isMobile = this.isMobile() const isMobile = this.isMobile()
if (isMobile) { if (isMobile) {
// 如果是移动端,通过 store.dispatch 方法触发 Vuex store 中的 'ToggleDevice' 这个 action传入'mobile' 字符串参数,可能用于在 store 中更新设备类型的状态为移动端,以便后续根据这个状态进行相应的布局或功能调整
store.dispatch('ToggleDevice', 'mobile') store.dispatch('ToggleDevice', 'mobile')
// 同时触发 'CloseSideBar' 这个 action传入 { withoutAnimation: true },表示关闭侧边栏并且关闭过程不显示动画,用于在移动端初始加载时关闭侧边栏以适应移动端布局等情况
store.dispatch('CloseSideBar', { withoutAnimation: true }) store.dispatch('CloseSideBar', { withoutAnimation: true })
} }
}, },
methods: { methods: {
// 定义一个名为 isMobile 的方法,用于判断当前设备是否为移动端
isMobile() { isMobile() {
// 通过 body.getBoundingClientRect() 方法获取 body 元素的尺寸信息(返回一个包含元素的位置、宽度、高度等信息的 DOMRect 对象)
const rect = body.getBoundingClientRect() const rect = body.getBoundingClientRect()
// 判断页面可视区域的宽度rect.width减去常量 RATIO 的值是否小于定义的 WIDTH1024px如果小于则认为当前设备是移动端返回 true否则返回 false
return rect.width - RATIO < WIDTH return rect.width - RATIO < WIDTH
}, },
// 定义 resizeHandler 方法,用于处理窗口大小变化的逻辑,当窗口触发'resize' 事件时会调用这个方法
resizeHandler() { resizeHandler() {
// 判断当前页面是否处于隐藏状态比如切换到其他标签页等情况document.hidden 为 true 表示页面被隐藏),如果页面没有隐藏
if (!document.hidden) { if (!document.hidden) {
// 调用 this.isMobile 方法重新判断当前设备是否变为移动端
const isMobile = this.isMobile() const isMobile = this.isMobile()
store.dispatch('ToggleDevice', isMobile ? 'mobile' : 'desktop') // 通过 store.dispatch 方法触发 Vuex store 中的 'ToggleDevice' 这个 action根据 isMobile 的结果传入'mobile' 或者 'desktop',用于更新 store 中设备类型的状态,使其与当前实际设备类型保持一致
store.dispatch('ToggleDevice', isMobile? 'mobile' : 'desktop')
if (isMobile) { if (isMobile) {
// 如果当前设备变为移动端,触发 'CloseSideBar' 这个 action传入 { withoutAnimation: true },关闭侧边栏并且不显示动画,以适应移动端布局要求
store.dispatch('CloseSideBar', { withoutAnimation: true }) store.dispatch('CloseSideBar', { withoutAnimation: true })
} }
} }

@ -1,26 +1,36 @@
<template> <template>
<!-- 最外层的 div 作为整个登录页面内容的容器 -->
<div> <div>
<!-- 使用 el-card 组件创建一个卡片式的布局容器用于包裹登录表单相关内容并设置了特定的类名 -->
<el-card class="login-form-layout"> <el-card class="login-form-layout">
<!-- el-form 组件用于创建表单以下是对其属性的设置说明 -->
<el-form autoComplete="on" <el-form autoComplete="on"
:model="loginForm" :model="loginForm"
:rules="loginRules" :rules="loginRules"
ref="loginForm" ref="loginForm"
label-position="left"> label-position="left">
<!-- div 元素用于将 SVG 图标在水平方向上居中显示 -->
<div style="text-align: center"> <div style="text-align: center">
<!-- svg-icon 组件用于展示 SVG 图标这里展示的图标类名为 "login-mall"并设置了宽度高度和颜色 -->
<svg-icon icon-class="login-mall" style="width: 56px;height: 56px;color: #409EFF"></svg-icon> <svg-icon icon-class="login-mall" style="width: 56px;height: 56px;color: #409EFF"></svg-icon>
</div> </div>
<!-- h2 标题元素展示登录页面的标题 "mall-admin-web"并设置了类名用于样式控制 -->
<h2 class="login-title color-main">mall-admin-web</h2> <h2 class="login-title color-main">mall-admin-web</h2>
<!-- el-form-item 组件表示表单中的一个项目这里对应 "用户名" 输入项通过 prop 属性关联验证规则 -->
<el-form-item prop="username"> <el-form-item prop="username">
<!-- el-input 组件用于创建输入框以下是对其属性和插槽的详细说明 -->
<el-input name="username" <el-input name="username"
type="text" type="text"
v-model="loginForm.username" v-model="loginForm.username"
autoComplete="on" autoComplete="on"
placeholder="请输入用户名"> placeholder="请输入用户名">
<span slot="prefix"> <!-- 使用插槽 "prefix" 在输入框前面添加一个 SVG 图标图标类名为 "user"并设置了类名用于样式控制 -->
<svg-icon icon-class="user" class="color-main"></svg-icon> <span slot="prefix">
</span> <svg-icon icon-class="user" class="color-main"></svg-icon>
</span>
</el-input> </el-input>
</el-form-item> </el-form-item>
<!-- 另一个 el-form-item 组件对应 "密码" 输入项同样通过 prop 属性关联验证规则 -->
<el-form-item prop="password"> <el-form-item prop="password">
<el-input name="password" <el-input name="password"
:type="pwdType" :type="pwdType"
@ -28,15 +38,19 @@
v-model="loginForm.password" v-model="loginForm.password"
autoComplete="on" autoComplete="on"
placeholder="请输入密码"> placeholder="请输入密码">
<span slot="prefix"> <!-- 同样使用插槽 "prefix" 在输入框前面添加表示密码的 SVG 图标图标类名为 "password"并设置类名用于样式控制 -->
<svg-icon icon-class="password" class="color-main"></svg-icon> <span slot="prefix">
</span> <svg-icon icon-class="password" class="color-main"></svg-icon>
</span>
<!-- 使用插槽 "suffix" 在输入框后面添加一个可点击的 SVG 图标点击这个图标可以切换密码的显示隐藏状态图标类名为 "eye"并设置类名用于样式控制 -->
<span slot="suffix" @click="showPwd"> <span slot="suffix" @click="showPwd">
<svg-icon icon-class="eye" class="color-main"></svg-icon> <svg-icon icon-class="eye" class="color-main"></svg-icon>
</span> </span>
</el-input> </el-input>
</el-form-item> </el-form-item>
<!-- 又是一个 el-form-item 组件用于放置登录和获取体验账号的按钮并设置了底部外边距和文本居中样式 -->
<el-form-item style="margin-bottom: 60px;text-align: center"> <el-form-item style="margin-bottom: 60px;text-align: center">
<!-- el-button 组件用于创建按钮以下是按钮的相关属性设置说明 -->
<el-button style="width: 45%" type="primary" :loading="loading" @click.native.prevent="handleLogin"> <el-button style="width: 45%" type="primary" :loading="loading" @click.native.prevent="handleLogin">
登录 登录
</el-button> </el-button>
@ -46,139 +60,202 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
<!-- img 标签用于展示一张背景图片图片的源地址通过绑定的变量 login_center_bg 来动态设置并设置了类名用于样式控制 -->
<img :src="login_center_bg" class="login-center-layout"> <img :src="login_center_bg" class="login-center-layout">
<!-- el-dialog 组件用于创建一个对话框以下是对其属性的详细说明 -->
<el-dialog <el-dialog
title="公众号二维码" title="公众号二维码"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
:show-close="false" :show-close="false"
:center="true" :center="true"
width="30%"> width="30%">
<!-- div 元素用于在对话框内放置相关内容并将文本居中显示 -->
<div style="text-align: center"> <div style="text-align: center">
<!-- span 元素用于展示提示文字通过设置不同的类名来控制文字的样式如字体大小颜色等提示用户关注公众号获取体验账号的操作流程 -->
<span class="font-title-large"><span class="color-main font-extra-large">关注公众号</span>回复<span class="color-main font-extra-large">体验</span>获取体验账号</span> <span class="font-title-large"><span class="color-main font-extra-large">关注公众号</span>回复<span class="color-main font-extra-large">体验</span>获取体验账号</span>
<br> <br>
<!-- 再次使用 img 标签在对话框内展示公众号的二维码图片设置了图片的宽度高度以及顶部外边距 -->
<img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg" width="160" height="160" style="margin-top: 10px"> <img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg" width="160" height="160" style="margin-top: 10px">
</div> </div>
<!-- 使用插槽 "footer" 在对话框底部定义按钮区域这里只有一个确定按钮 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogConfirm"></el-button> <el-button type="primary" @click="dialogConfirm"></el-button>
</span> </span>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import {isvalidUsername} from '@/utils/validate'; // @/utils/validate isvalidUsername
import {setSupport,getSupport,setCookie,getCookie} from '@/utils/support'; import {isvalidUsername} from '@/utils/validate';
import login_center_bg from '@/assets/images/login_center_bg.png' // @/utils/support Cookie
import {setSupport, getSupport, setCookie, getCookie} from '@/utils/support';
//
import login_center_bg from '@/assets/images/login_center_bg.png'
export default { export default {
name: 'login', name: 'login',
data() { data() {
const validateUsername = (rule, value, callback) => { // validateUsername
if (!isvalidUsername(value)) { const validateUsername = (rule, value, callback) => {
callback(new Error('请输入正确的用户名')) // isvalidUsername
} else { if (!isvalidUsername(value)) {
callback() // callback Error
} callback(new Error('请输入正确的用户名'))
}; } else {
const validatePass = (rule, value, callback) => { //
if (value.length < 3) { callback()
callback(new Error('密码不能小于3位'))
} else {
callback()
}
};
return {
loginForm: {
username: '',
password: '',
},
loginRules: {
username: [{required: true, trigger: 'blur', validator: validateUsername}],
password: [{required: true, trigger: 'blur', validator: validatePass}]
},
loading: false,
pwdType: 'password',
login_center_bg,
dialogVisible:false,
supportDialogVisible:false
} }
}, };
created() { // validatePass
this.loginForm.username = getCookie("username"); const validatePass = (rule, value, callback) => {
this.loginForm.password = getCookie("password"); // 3
if(this.loginForm.username === undefined||this.loginForm.username==null||this.loginForm.username===''){ if (value.length < 3) {
this.loginForm.username = 'admin'; // callback Error 3
callback(new Error('密码不能小于3位'))
} else {
//
callback()
} }
if(this.loginForm.password === undefined||this.loginForm.password==null){ };
this.loginForm.password = ''; return {
// loginForm
loginForm: {
username: '',
password: '',
},
// loginRules
loginRules: {
// blur validateUsername
username: [{required: true, trigger: 'blur', validator: validateUsername}],
// validatePass
password: [{required: true, trigger: 'blur', validator: validatePass}]
},
// loading false
loading: false,
// pwdType 'password'
pwdType: 'password',
//
login_center_bg,
// dialogVisible false
dialogVisible: false,
// supportDialogVisible 使 false
supportDialogVisible: false
}
},
created() {
// Cookie loginForm.username
this.loginForm.username = getCookie("username");
// Cookie loginForm.password
this.loginForm.password = getCookie("password");
// undefinednull
if (this.loginForm.username === undefined || this.loginForm.username == null || this.loginForm.username === '') {
// 'admin'
this.loginForm.username = 'admin';
}
// undefined null
if (this.loginForm.password === undefined || this.loginForm.password == null) {
//
this.loginForm.password = '';
}
},
methods: {
showPwd() {
// pwdType 'password'
if (this.pwdType === 'password') {
//
this.pwdType = ''
} else {
// 'password'
this.pwdType = 'password'
} }
}, },
methods: { handleLogin() {
showPwd() { // loginForm
if (this.pwdType === 'password') { this.$refs.loginForm.validate(valid => {
this.pwdType = '' if (valid) {
// let isSupport = getSupport();
// if (isSupport === undefined || isSupport == null) {
// this.dialogVisible = true;
// return;
// }
// loading true
this.loading = true;
// $store.dispatch 'Login' action loginForm
this.$store.dispatch('Login', this.loginForm).then(() => {
// loading false
this.loading = false;
// Cookie 15 15
setCookie("username", this.loginForm.username, 15);
// Cookie 15
setCookie("password", this.loginForm.password, 15);
// $router.push '/'
this.$router.push({path: '/'})
}).catch(() => {
// loading false
this.loading = false
})
} else { } else {
this.pwdType = 'password' // false
console.log('参数验证不合法!');
return false
} }
}, })
handleLogin() { },
this.$refs.loginForm.validate(valid => { handleTry() {
if (valid) { // dialogVisible true
// let isSupport = getSupport(); this.dialogVisible = true
// if(isSupport===undefined||isSupport==null){ },
// this.dialogVisible =true; dialogConfirm() {
// return; // dialogVisible false
// } this.dialogVisible = false;
this.loading = true; // setSupport true setSupport
this.$store.dispatch('Login', this.loginForm).then(() => { setSupport(true);
this.loading = false; },
setCookie("username",this.loginForm.username,15); dialogCancel() {
setCookie("password",this.loginForm.password,15); // dialogVisible false
this.$router.push({path: '/'}) this.dialogVisible = false;
}).catch(() => { // setSupport false setSupport
this.loading = false setSupport(false);
})
} else {
console.log('参数验证不合法!');
return false
}
})
},
handleTry(){
this.dialogVisible =true
},
dialogConfirm(){
this.dialogVisible =false;
setSupport(true);
},
dialogCancel(){
this.dialogVisible = false;
setSupport(false);
}
} }
} }
}
</script> </script>
<style scoped> <style scoped>
.login-form-layout { /* 为类名为 "login-form-layout" 的元素设置样式,以下是具体样式属性的说明 */
position: absolute; .login-form-layout {
left: 0; /* 将元素定位设置为绝对定位,使其可以相对于最近的已定位祖先元素进行定位 */
right: 0; position: absolute;
width: 360px; /* 使其在水平方向上相对于父元素左对齐 */
margin: 140px auto; left: 0;
border-top: 10px solid #409EFF; /* 使其在水平方向上相对于父元素右对齐 */
} right: 0;
/* 设置元素的宽度为 360px */
width: 360px;
/* 设置元素在垂直方向上居中,通过上下外边距 auto 实现 */
margin: 140px auto;
/* 设置元素的上边框样式,宽度为 10px颜色为 #409EFF */
border-top: 10px solid #409EFF;
}
.login-title { /* 为类名为 "login-title" 的元素设置样式,这里只是简单地将文本在元素内居中显示 */
text-align: center; .login-title {
} text-align: center;
}
.login-center-layout { /* 为类名为 "login-center-layout" 的元素设置样式,以下是具体样式属性的说明 */
background: #409EFF; .login-center-layout {
width: auto; /* 设置元素的背景颜色为 #409EFF */
height: auto; background: #409EFF;
max-width: 100%; /* 设置元素的宽度自动适应内容,不会超出父元素宽度 */
max-height: 100%; width: auto;
margin-top: 200px; /* 设置元素的高度自动适应内容,不会超出父元素高度 */
} height: auto;
/* 设置元素的最大宽度为 100%,即不会超过父元素的宽度 */
max-width: 100%;
/* 设置元素的最大高度为 100%,即不会超过父元素的高度 */
max-height: 100%;
/* 设置元素的顶部外边距为 200px */
margin-top: 200px;
}
</style> </style>

@ -1,9 +1,13 @@
<template>  <template>
<!-- 应用容器 -->
<div class="app-container"> <div class="app-container">
<!-- 筛选搜索区域 -->
<el-card class="filter-container" shadow="never"> <el-card class="filter-container" shadow="never">
<div> <div>
<!-- 搜索图标和文本 -->
<i class="el-icon-search"></i> <i class="el-icon-search"></i>
<span>筛选搜索</span> <span>筛选搜索</span>
<!-- 查询搜索按钮 -->
<el-button <el-button
style="float:right" style="float:right"
type="primary" type="primary"
@ -11,6 +15,7 @@
size="small"> size="small">
查询搜索 查询搜索
</el-button> </el-button>
<!-- 重置按钮 -->
<el-button <el-button
style="float:right;margin-right: 15px" style="float:right;margin-right: 15px"
@click="handleResetSearch()" @click="handleResetSearch()"
@ -18,11 +23,14 @@
重置 重置
</el-button> </el-button>
</div> </div>
<!-- 表单区域用于输入筛选条件 -->
<div style="margin-top: 15px"> <div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px"> <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 输入搜索项 -->
<el-form-item label="输入搜索:"> <el-form-item label="输入搜索:">
<el-input v-model="listQuery.id" class="input-width" placeholder="服务单号"></el-input> <el-input v-model="listQuery.id" class="input-width" placeholder="服务单号"></el-input>
</el-form-item> </el-form-item>
<!-- 处理状态下拉选择 -->
<el-form-item label="处理状态:"> <el-form-item label="处理状态:">
<el-select v-model="listQuery.status" placeholder="全部" clearable class="input-width"> <el-select v-model="listQuery.status" placeholder="全部" clearable class="input-width">
<el-option v-for="item in statusOptions" <el-option v-for="item in statusOptions"
@ -32,6 +40,7 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 申请时间选择 -->
<el-form-item label="申请时间:"> <el-form-item label="申请时间:">
<el-date-picker <el-date-picker
class="input-width" class="input-width"
@ -41,9 +50,11 @@
placeholder="请选择时间"> placeholder="请选择时间">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
<!-- 操作人员输入 -->
<el-form-item label="操作人员:"> <el-form-item label="操作人员:">
<el-input v-model="listQuery.handleMan" class="input-width" placeholder="全部"></el-input> <el-input v-model="listQuery.handleMan" class="input-width" placeholder="全部"></el-input>
</el-form-item> </el-form-item>
<!-- 处理时间选择 -->
<el-form-item label="处理时间:"> <el-form-item label="处理时间:">
<el-date-picker <el-date-picker
class="input-width" class="input-width"
@ -56,45 +67,58 @@
</el-form> </el-form>
</div> </div>
</el-card> </el-card>
<!-- 数据列表操作区域 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i> <i class="el-icon-tickets"></i>
<span>数据列表</span> <span>数据列表</span>
</el-card> </el-card>
<!-- 表格容器 -->
<div class="table-container"> <div class="table-container">
<!-- 表格展示数据列表 -->
<el-table ref="returnApplyTable" <el-table ref="returnApplyTable"
:data="list" :data="list"
style="width: 100%;" style="width: 100%;"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
v-loading="listLoading" border> v-loading="listLoading" border>
<!-- 选择列 -->
<el-table-column type="selection" width="60" align="center"></el-table-column> <el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 服务单号列 -->
<el-table-column label="服务单号" width="180" align="center"> <el-table-column label="服务单号" width="180" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column> </el-table-column>
<!-- 申请时间列 -->
<el-table-column label="申请时间" width="180" align="center"> <el-table-column label="申请时间" width="180" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatTime}}</template> <template slot-scope="scope">{{scope.row.createTime | formatTime}}</template>
</el-table-column> </el-table-column>
<!-- 用户账号列 -->
<el-table-column label="用户账号" align="center"> <el-table-column label="用户账号" align="center">
<template slot-scope="scope">{{scope.row.memberUsername}}</template> <template slot-scope="scope">{{scope.row.memberUsername}}</template>
</el-table-column> </el-table-column>
<!-- 退款金额列 -->
<el-table-column label="退款金额" width="180" align="center"> <el-table-column label="退款金额" width="180" align="center">
<template slot-scope="scope">{{scope.row | formatReturnAmount}}</template> <template slot-scope="scope">{{scope.row | formatReturnAmount}}</template>
</el-table-column> </el-table-column>
<!-- 申请状态列 -->
<el-table-column label="申请状态" width="180" align="center"> <el-table-column label="申请状态" width="180" align="center">
<template slot-scope="scope">{{scope.row.status | formatStatus}}</template> <template slot-scope="scope">{{scope.row.status | formatStatus}}</template>
</el-table-column> </el-table-column>
<!-- 处理时间列 -->
<el-table-column label="处理时间" width="180" align="center"> <el-table-column label="处理时间" width="180" align="center">
<template slot-scope="scope">{{scope.row.handleTime | formatTime}}</template> <template slot-scope="scope">{{scope.row.handleTime | formatTime}}</template>
</el-table-column> </el-table-column>
<!-- 操作列 -->
<el-table-column label="操作" width="180" align="center"> <el-table-column label="操作" width="180" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
size="mini" size="mini"
@click="handleViewDetail(scope.$index, scope.row)">查看详情</el-button> @click="handleViewDetail(scope.$index, scope.row)">查看详情</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 批量操作容器 -->
<div class="batch-operate-container"> <div class="batch-operate-container">
<!-- 批量操作下拉选择 -->
<el-select <el-select
size="small" size="small"
v-model="operateType" placeholder="批量操作"> v-model="operateType" placeholder="批量操作">
@ -105,6 +129,7 @@
:value="item.value"> :value="item.value">
</el-option> </el-option>
</el-select> </el-select>
<!-- 批量操作按钮 -->
<el-button <el-button
style="margin-left: 20px" style="margin-left: 20px"
class="search-button" class="search-button"
@ -114,6 +139,7 @@
确定 确定
</el-button> </el-button>
</div> </div>
<!-- 分页容器 -->
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination
background background
@ -128,9 +154,14 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script>
//
import {formatDate} from '@/utils/date'; import {formatDate} from '@/utils/date';
// API
import {fetchList,deleteApply} from '@/api/returnApply'; import {fetchList,deleteApply} from '@/api/returnApply';
//
const defaultListQuery = { const defaultListQuery = {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
@ -141,6 +172,7 @@
handleMan: null, handleMan: null,
handleTime: null handleTime: null
}; };
//
const defaultStatusOptions=[ const defaultStatusOptions=[
{ {
label: '待处理', label: '待处理',
@ -159,6 +191,7 @@
value: 3 value: 3
} }
]; ];
export default { export default {
name:'returnApplyList', name:'returnApplyList',
data() { data() {
@ -179,9 +212,11 @@
} }
}, },
created(){ created(){
//
this.getList(); this.getList();
}, },
filters:{ filters:{
//
formatTime(time) { formatTime(time) {
if(time==null||time===''){ if(time==null||time===''){
return 'N/A'; return 'N/A';
@ -189,6 +224,7 @@
let date = new Date(time); let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss') return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}, },
//
formatStatus(status){ formatStatus(status){
for(let i=0;i<defaultStatusOptions.length;i++){ for(let i=0;i<defaultStatusOptions.length;i++){
if(status===defaultStatusOptions[i].value){ if(status===defaultStatusOptions[i].value){
@ -196,24 +232,30 @@
} }
} }
}, },
// 退
formatReturnAmount(row){ formatReturnAmount(row){
return row.productRealPrice*row.productCount; return row.productRealPrice*row.productCount;
} }
}, },
methods:{ methods:{
//
handleSelectionChange(val){ handleSelectionChange(val){
this.multipleSelection = val; this.multipleSelection = val;
}, },
//
handleResetSearch() { handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery); this.listQuery = Object.assign({}, defaultListQuery);
}, },
//
handleSearchList() { handleSearchList() {
this.listQuery.pageNum = 1; this.listQuery.pageNum = 1;
this.getList(); this.getList();
}, },
//
handleViewDetail(index,row){ handleViewDetail(index,row){
this.$router.push({path:'/oms/returnApplyDetail',query:{id:row.id}}) this.$router.push({path:'/oms/returnApplyDetail',query:{id:row.id}})
}, },
//
handleBatchOperate(){ handleBatchOperate(){
if(this.multipleSelection==null||this.multipleSelection.length<1){ if(this.multipleSelection==null||this.multipleSelection.length<1){
this.$message({ this.$message({
@ -224,7 +266,7 @@
return; return;
} }
if(this.operateType===1){ if(this.operateType===1){
// //
this.$confirm('是否要进行删除操作?', '提示', { this.$confirm('是否要进行删除操作?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',

@ -1,59 +1,69 @@
<template>  <template>
<el-dialog title="订单跟踪" <!-- 使用 el-dialog 组件创建一个对话框用于展示订单跟踪相关信息设置对话框的标题为 "订单跟踪"通过 :visible.sync 双向绑定对话框的可见性与组件的 visible 属性关联后面会详细介绍绑定 :before-close 事件处理函数用于在对话框关闭前执行一些操作设置对话框宽度为 40% -->
:visible.sync="visible" <el-dialog title="订单跟踪"
:before-close="handleClose" :visible.sync="visible"
width="40%"> :before-close="handleClose"
<el-steps direction="vertical" width="40%">
:active="6" <!-- el-steps 组件用于创建一个步骤条设置方向为垂直direction="vertical"指定当前激活的步骤索引为 6:active="6"这里可能需要根据实际业务逻辑根据订单物流状态动态设置该值设置已完成步骤的状态样式为 "success"通常表示成功完成的视觉效果比如显示为绿色等设置每个步骤之间的间隔为 50pxspace="50px" -->
finish-status="success" <el-steps direction="vertical"
space="50px"> :active="6"
<el-step v-for="item in logisticsList" finish-status="success"
:key="item.name" space="50px">
:title="item.name" <!-- 通过 v-for 指令循环遍历 logisticsList 数据数组来动态生成每个步骤el-step每个步骤绑定一个唯一的 key使用 :key="item.name" Vue 中循环渲染列表时key 是必要的有助于 Vue 更高效地更新虚拟 DOM设置步骤的标题为 item.name从数据中获取每个步骤对应的名称设置步骤的描述信息为 item.time从数据中获取对应时间信息 -->
:description="item.time"></el-step> <el-step v-for="item in logisticsList"
</el-steps> :key="item.name"
</el-dialog> :title="item.name"
:description="item.time"></el-step>
</el-steps>
</el-dialog>
</template> </template>
<script> <script>
const defaultLogisticsList=[ // defaultLogisticsListname time
{name: '订单已提交,等待付款',time:'2017-04-01 12:00:00 '}, const defaultLogisticsList = [
{name: '订单付款成功',time:'2017-04-01 12:00:00 '}, {name: '订单已提交,等待付款', time: '2017-04-01 12:00:00 '},
{name: '在北京市进行下级地点扫描,等待付款',time:'2017-04-01 12:00:00 '}, {name: '订单付款成功', time: '2017-04-01 12:00:00 '},
{name: '在分拨中心广东深圳公司进行卸车扫描,等待付款',time:'2017-04-01 12:00:00 '}, {name: '在北京市进行下级地点扫描,等待付款', time: '2017-04-01 12:00:00 '},
{name: '在广东深圳公司进行发出扫描',time:'2017-04-01 12:00:00 '}, {name: '在分拨中心广东深圳公司进行卸车扫描,等待付款', time: '2017-04-01 12:00:00 '},
{name: '到达目的地网点广东深圳公司,快件将很快进行派送',time:'2017-04-01 12:00:00 '}, {name: '在广东深圳公司进行发出扫描', time: '2017-04-01 12:00:00 '},
{name: '订单已签收,期待再次为您服务',time:'2017-04-01 12:00:00 '} {name: '到达目的地网点广东深圳公司,快件将很快进行派送', time: '2017-04-01 12:00:00 '},
]; {name: '订单已签收,期待再次为您服务', time: '2017-04-01 12:00:00 '}
export default { ];
name:'logisticsDialog',
props: { // Vue
value: Boolean export default {
}, // Vue 'logisticsDialog'
computed:{ name: 'logisticsDialog',
visible: { // props value Vue
get() { props: {
return this.value; value: Boolean
}, },
set(visible){ computed: {
this.value=visible; // visible get set props value Vue props 便
} visible: {
} get() {
}, return this.value;
data() { },
return { set(visible) {
logisticsList:Object.assign({},defaultLogisticsList) this.value = visible;
} }
}, }
methods:{ },
emitInput(val) { data() {
this.$emit('input', val) return {
}, // defaultLogisticsList Object.assign logisticsList
handleClose(){ logisticsList: Object.assign({}, defaultLogisticsList)
this.emitInput(false); }
},
methods: {
// emitInput 'input' val Vue $emit
emitInput(val) {
this.$emit('input', val)
},
// handleClose el-dialog :before-close emitInput false
handleClose() {
this.emitInput(false);
}
}
} }
} </script>
}
</script>
<style></style> <style></style>

@ -1,99 +1,130 @@
<template>  <template>
<div class="app-container"> <!-- 创建一个类名为 "app-container" div 作为整体页面的容器 -->
<el-card class="operate-container" shadow="never"> <div class="app-container">
<i class="el-icon-tickets"></i> <!-- 使用 el-card 组件创建一个卡片式容器设置类名为 "operate-container" 并取消阴影效果用于展示页面相关标题 -->
<span>发货列表</span> <el-card class="operate-container" shadow="never">
</el-card> <!-- 使用一个图标元素el-icon-tickets展示一个图标 -->
<div class="table-container"> <i class="el-icon-tickets"></i>
<el-table ref="deliverOrderTable" <!-- 展示一段文本内容为 "发货列表"用于表明该页面主要展示的内容 -->
style="width: 100%;" <span>发货列表</span>
:data="list" border> </el-card>
<el-table-column label="订单编号" width="180" align="center"> <!-- 创建一个类名为 "table-container" div用于放置展示发货相关信息的表格以及操作按钮等内容 -->
<template slot-scope="scope">{{scope.row.orderSn}}</template> <div class="table-container">
</el-table-column> <!-- el-table 组件创建一个表格设置引用名称宽度以及绑定数据等属性同时设置表格显示边框 -->
<el-table-column label="收货人" width="180" align="center"> <el-table ref="deliverOrderTable"
<template slot-scope="scope">{{scope.row.receiverName}}</template> style="width: 100%;"
</el-table-column> :data="list" border>
<el-table-column label="手机号码" width="160" align="center"> <!-- 定义表格列用于展示订单编号信息设置宽度和文本对齐方式通过插槽作用域获取对应行数据中的订单编号并展示 -->
<template slot-scope="scope">{{scope.row.receiverPhone}}</template> <el-table-column label="订单编号" width="180" align="center">
</el-table-column> <template slot-scope="scope">{{scope.row.orderSn}}</template>
<el-table-column label="邮政编码" width="160" align="center"> </el-table-column>
<template slot-scope="scope">{{scope.row.receiverPostCode}}</template> <!-- 定义表格列用于展示收货人信息设置宽度和文本对齐方式通过插槽作用域获取对应行数据中的收货人姓名并展示 -->
</el-table-column> <el-table-column label="收货人" width="180" align="center">
<el-table-column label="收货地址" align="center"> <template slot-scope="scope">{{scope.row.receiverName}}</template>
<template slot-scope="scope">{{scope.row.address}}</template> </el-table-column>
</el-table-column> <!-- 定义表格列用于展示手机号码信息设置宽度和文本对齐方式通过插槽作用域获取对应行数据中的手机号码并展示 -->
<el-table-column label="配送方式" width="160" align="center"> <el-table-column label="手机号码" width="160" align="center">
<template slot-scope="scope"> <template slot-scope="scope">{{scope.row.receiverPhone}}</template>
<el-select placeholder="请选择物流公司" </el-table-column>
v-model="scope.row.deliveryCompany" <!-- 定义表格列用于展示邮政编码信息设置宽度和文本对齐方式通过插槽作用域获取对应行数据中的邮政编码并展示 -->
size="small"> <el-table-column label="邮政编码" width="160" align="center">
<el-option v-for="item in companyOptions" <template slot-scope="scope">{{scope.row.receiverPostCode}}</template>
:key="item" </el-table-column>
:label="item" <!-- 定义表格列用于展示收货地址信息设置文本对齐方式通过插槽作用域获取对应行数据中的收货地址并展示 -->
:value="item"> <el-table-column label="收货地址" align="center">
</el-option> <template slot-scope="scope">{{scope.row.address}}</template>
</el-select> </el-table-column>
</template> <!-- 定义表格列用于展示配送方式信息通过插槽作用域在每个单元格内创建一个下拉选择框用于选择物流公司绑定对应行数据中的 deliveryCompany 属性设置占位提示文本和小尺寸样式通过循环 companyOptions 数据动态生成下拉选项 -->
</el-table-column> <el-table-column label="配送方式" width="160" align="center">
<el-table-column label="物流单号" width="180" align="center"> <template slot-scope="scope">
<template slot-scope="scope"> <el-select placeholder="请选择物流公司"
<el-input size="small" v-model="scope.row.deliverySn"></el-input> v-model="scope.row.deliveryCompany"
</template> size="small">
</el-table-column> <el-option v-for="item in companyOptions"
</el-table> :key="item"
<div style="margin-top: 15px;text-align: center"> :label="item"
<el-button @click="cancel"></el-button> :value="item">
<el-button @click="confirm" type="primary">确定</el-button> </el-option>
</div> </el-select>
</template>
</el-table-column>
<!-- 定义表格列用于展示物流单号信息通过插槽作用域在每个单元格内创建一个小尺寸的输入框双向绑定对应行数据中的 deliverySn 属性用于输入物流单号 -->
<el-table-column label="物流单号" width="180" align="center">
<template slot-scope="scope">
<el-input size="small" v-model="scope.row.deliverySn"></el-input>
</template>
</el-table-column>
</el-table>
<!-- 创建一个 div设置上边距文本居中对齐用于放置操作按钮 -->
<div style="margin-top: 15px;text-align: center">
<!-- el-button 组件创建一个按钮点击时调用 "cancel" 方法按钮文本为 "取消" -->
<el-button @click="cancel"></el-button>
<!-- el-button 组件创建一个按钮点击时调用 "confirm" 方法按钮类型设置为 "primary"通常表示主要操作按钮样式上可能会突出显示按钮文本为 "确定" -->
<el-button @click="confirm" type="primary">确定</el-button>
</div>
</div>
</div> </div>
</div>
</template> </template>
<script>
import {deliveryOrder} from '@/api/order'
const defaultLogisticsCompanies=["顺丰快递","圆通快递","中通快递","韵达快递"];
export default {
name: 'deliverOrderList',
data() {
return {
list:[],
companyOptions:defaultLogisticsCompanies
}
},
created(){
this.list= this.$route.query.list;
//list
if(this.list instanceof Array===false){
this.list=[];
}
},
methods:{
cancel(){
this.$router.back();
},
confirm(){
this.$confirm('是否要进行发货操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deliveryOrder(this.list).then(response=>{
this.$router.back();
this.$message({
type: 'success',
message: '发货成功!'
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消发货'
});
});
}
}
}
</script>
<style></style>
<script>
// '@/api/order' deliveryOrder
import {deliveryOrder} from '@/api/order';
//
const defaultLogisticsCompanies = ["顺丰快递", "圆通快递", "中通快递", "韵达快递"];
// Vue
export default {
// Vue "deliverOrderList"
name: 'deliverOrderList',
data() {
return {
//
list: [],
// defaultLogisticsCompanies
companyOptions: defaultLogisticsCompanies
}
},
created() {
// this.$route.query "list" list
this.list = this.$route.query.list;
// list list
if (this.list instanceof Array === false) {
this.list = [];
}
},
methods: {
// "cancel" Vue this.$router back
cancel() {
this.$router.back();
}
// "confirm"
confirm() {
// 使 $confirm "warning"
this.$confirm('是否要进行发货操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// "" "deliveryOrder" list
// then Vue this.$router back 使 $message "!"
deliveryOrder(this.list).then(response => {
this.$router.back();
this.$message({
type: 'success',
message: '发货成功!'
});
});
}).catch(() => {
// "" catch 使 $message ""
this.$message({
type: 'info',
message: '已取消发货'
});
});
}
}
}
</script>
<style></style>

@ -1,402 +1,550 @@
<template>  <template>
<div class="app-container"> <!-- 创建一个类名为 "app-container" div 作为整体页面容器 -->
<el-card class="filter-container" shadow="never"> <div class="app-container">
<div> <!-- 使用 el-card 组件创建一个卡片式容器用于放置筛选搜索相关内容设置类名为 "filter-container" 并取消阴影效果 -->
<i class="el-icon-search"></i> <el-card class="filter-container" shadow="never">
<span>筛选搜索</span> <div>
<el-button <!-- 使用一个图标元素el-icon-search展示搜索图标 -->
style="float:right" <i class="el-icon-search"></i>
type="primary" <!-- 展示一段文本内容为 "筛选搜索" -->
@click="handleSearchList()" <span>筛选搜索</span>
size="small"> <!-- el-button 组件创建一个按钮设置样式使其右浮动按钮类型为 "primary"点击时调用 "handleSearchList" 方法按钮大小为 "small"文本为 "查询搜索" -->
查询搜索 <el-button
</el-button> style="float:right"
<el-button type="primary"
style="float:right;margin-right: 15px" @click="handleSearchList()"
@click="handleResetSearch()" size="small">
size="small"> 查询搜索
重置 </el-button>
</el-button> <!-- 类似上述按钮设置右浮动及右外边距点击调用 "handleResetSearch" 方法用于重置筛选条件按钮大小为 "small"文本为 "重置" -->
</div> <el-button
<div style="margin-top: 15px"> style="float:right;margin-right: 15px"
<el-form :inline="true" :model="listQuery" size="small" label-width="140px"> @click="handleResetSearch()"
<el-form-item label="输入搜索:"> size="small">
<el-input v-model="listQuery.orderSn" class="input-width" placeholder="订单编号"></el-input> 重置
</el-form-item> </el-button>
<el-form-item label="收货人:"> </div>
<el-input v-model="listQuery.receiverKeyword" class="input-width" placeholder="收货人姓名/手机号码"></el-input> <div style="margin-top: 15px">
</el-form-item> <!-- el-form 组件创建一个内联表单绑定数据模型为 "listQuery"设置表单大小为 "small"标签宽度为 "140px" -->
<el-form-item label="提交时间:"> <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-date-picker <!-- el-form-item 组件定义表单中的一项用于输入订单编号的搜索框双向绑定 "listQuery.orderSn"设置类名控制宽度添加占位提示文本 -->
class="input-width" <el-form-item label="输入搜索:">
v-model="listQuery.createTime" <el-input v-model="listQuery.orderSn" class="input-width" placeholder="订单编号"></el-input>
value-format="yyyy-MM-dd" </el-form-item>
type="date" <!-- 类似上述用于输入收货人相关关键字的搜索框绑定 "listQuery.receiverKeyword" -->
placeholder="请选择时间"> <el-form-item label="收货人:">
</el-date-picker> <el-input v-model="listQuery.receiverKeyword" class="input-width" placeholder="收货人姓名/手机号码"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="订单状态:"> <!-- 用于选择提交时间的日期选择器绑定 "listQuery.createTime"设置日期格式类型为 "date"仅选择日期及占位提示文本通过类名控制宽度 -->
<el-select v-model="listQuery.status" class="input-width" placeholder="全部" clearable> <el-form-item label="提交时间:">
<el-option v-for="item in statusOptions" <el-date-picker
:key="item.value" class="input-width"
:label="item.label" v-model="listQuery.createTime"
:value="item.value"> value-format="yyyy-MM-dd"
</el-option> type="date"
placeholder="请选择时间">
</el-date-picker>
</el-form-item>
<!-- 用于选择订单状态的下拉选择框绑定 "listQuery.status"设置类名控制宽度占位提示文本并允许清空选项通过循环选项数据动态生成下拉选项 -->
<el-form-item label="订单状态:">
<el-select v-model="listQuery.status" class="input-width" placeholder="全部" clearable>
<el-option v-for="item in statusOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 用于选择订单分类的下拉选择框绑定 "listQuery.orderType"类似上述设置 -->
<el-form-item label="订单分类:">
<el-select v-model="listQuery.orderType" class="input-width" placeholder="全部" clearable>
<el-option v-for="item in orderTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 用于选择订单来源的下拉选择框绑定 "listQuery.sourceType"同样的设置方式 -->
<el-form-item label="订单来源:">
<el-select v-model="listQuery.sourceType" class="input-width" placeholder="全部" clearable>
<el-option v-for="item in sourceTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-form>
</div>
</el-card>
<!-- 使用 el-card 组件创建另一个卡片式容器用于展示数据列表相关的标题设置类名为 "operate-container" 并取消阴影效果 -->
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
</el-card>
<!-- 创建一个类名为 "table-container" div用于放置展示数据的表格 -->
<div class="table-container">
<!-- el-table 组件创建表格设置引用绑定数据宽度监听行选择变化事件以及控制加载状态等属性 -->
<el-table ref="orderTable"
:data="list"
style="width: 100%;"
@selection-change="handleSelectionChange"
v-loading="listLoading" border>
<!-- 定义一个选择列用于多选表格行设置宽度和文本对齐方式 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 定义表格列展示编号信息设置宽度和文本对齐方式通过插槽作用域获取对应行数据展示 -->
<el-table-column label="编号" width="80" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 定义表格列展示订单编号信息设置宽度和文本对齐方式 -->
<el-table-column label="订单编号" width="180" align="center">
<template slot-scope="scope">{{scope.row.orderSn}}</template>
</el-table-column>
<!-- 定义表格列展示提交时间信息设置宽度和文本对齐方式通过管道符调用 formatCreateTime 过滤器对时间进行格式化后展示 -->
<el-table-column label="提交时间" width="180" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatCreateTime}}</template>
</el-table-column>
<!-- 定义表格列展示用户账号信息设置文本对齐方式通过插槽作用域获取对应行数据展示 -->
<el-table-column label="用户账号" align="center">
<template slot-scope="scope">{{scope.row.memberUsername}}</template>
</el-table-column>
<!-- 定义表格列展示订单金额信息设置宽度和文本对齐方式 -->
<el-table-column label="订单金额" width="120" align="center">
<template slot-scope="scope">{{scope.row.totalAmount}}</template>
</el-table-column>
<!-- 定义表格列展示支付方式信息设置宽度和文本对齐方式通过管道符调用 formatPayType 过滤器对支付方式进行格式化后展示 -->
<el-table-column label="支付方式" width="120" align="center">
<template slot-scope="scope">{{scope.row.payType | formatPayType}}</template>
</el-table-column>
<!-- 定义表格列展示订单来源信息设置宽度和文本对齐方式通过管道符调用 formatSourceType 过滤器对订单来源进行格式化后展示 -->
<el-table-column label="订单来源" width="120" align="center">
<template slot-scope="scope">{{scope.row.sourceType | formatSourceType}}</template>
</el-table-column>
<!-- 定义表格列展示订单状态信息设置宽度和文本对齐方式通过管道符调用 formatStatus 过滤器对订单状态进行格式化后展示 -->
<el-table-column label="订单状态" width="120" align="center">
<template slot-scope="scope">{{scope.row.status | formatStatus}}</template>
</el-table-column>
<!-- 定义表格列展示操作按钮设置宽度和文本对齐方式通过插槽作用域根据不同订单状态动态显示相应操作按钮 -->
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleViewOrder(scope.$index, scope.row)"
>查看订单</el-button>
<el-button
size="mini"
@click="handleCloseOrder(scope.$index, scope.row)"
v-show="scope.row.status===0">关闭订单</el-button>
<el-button
size="mini"
@click="handleDeliveryOrder(scope.$index, scope.row)"
v-show="scope.row.status===1">订单发货</el-button>
<el-button
size="mini"
@click="handleViewLogistics(scope.$index, scope.row)"
v-show="scope.row.status===2||scope.row.status===3">订单跟踪</el-button>
<el-button
size="mini"
type="danger"
@click="handleDeleteOrder(scope.$index, scope.row)"
v-show="scope.row.status===4">删除订单</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 创建一个类名为 "batch-operate-container" div用于放置批量操作相关的选择框和按钮 -->
<div class="batch-operate-container">
<!-- el-select 组件创建下拉选择框设置大小绑定数据模型通过循环选项数据动态生成下拉选项 -->
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operateOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select> </el-select>
</el-form-item> <!-- el-button 组件创建按钮设置左外边距类名点击调用 "handleBatchOperate" 方法按钮类型为 "primary" 以及按钮大小为 "small"文本为 "确定" -->
<el-form-item label="订单分类:">
<el-select v-model="listQuery.orderType" class="input-width" placeholder="全部" clearable>
<el-option v-for="item in orderTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="订单来源:">
<el-select v-model="listQuery.sourceType" class="input-width" placeholder="全部" clearable>
<el-option v-for="item in sourceTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
</el-card>
<div class="table-container">
<el-table ref="orderTable"
:data="list"
style="width: 100%;"
@selection-change="handleSelectionChange"
v-loading="listLoading" border>
<el-table-column type="selection" width="60" align="center"></el-table-column>
<el-table-column label="编号" width="80" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="订单编号" width="180" align="center">
<template slot-scope="scope">{{scope.row.orderSn}}</template>
</el-table-column>
<el-table-column label="提交时间" width="180" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatCreateTime}}</template>
</el-table-column>
<el-table-column label="用户账号" align="center">
<template slot-scope="scope">{{scope.row.memberUsername}}</template>
</el-table-column>
<el-table-column label="订单金额" width="120" align="center">
<template slot-scope="scope">{{scope.row.totalAmount}}</template>
</el-table-column>
<el-table-column label="支付方式" width="120" align="center">
<template slot-scope="scope">{{scope.row.payType | formatPayType}}</template>
</el-table-column>
<el-table-column label="订单来源" width="120" align="center">
<template slot-scope="scope">{{scope.row.sourceType | formatSourceType}}</template>
</el-table-column>
<el-table-column label="订单状态" width="120" align="center">
<template slot-scope="scope">{{scope.row.status | formatStatus}}</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleViewOrder(scope.$index, scope.row)"
>查看订单</el-button>
<el-button
size="mini"
@click="handleCloseOrder(scope.$index, scope.row)"
v-show="scope.row.status===0">关闭订单</el-button>
<el-button
size="mini"
@click="handleDeliveryOrder(scope.$index, scope.row)"
v-show="scope.row.status===1">订单发货</el-button>
<el-button
size="mini"
@click="handleViewLogistics(scope.$index, scope.row)"
v-show="scope.row.status===2||scope.row.status===3">订单跟踪</el-button>
<el-button <el-button
size="mini" style="margin-left: 20px"
type="danger" class="search-button"
@click="handleDeleteOrder(scope.$index, scope.row)" @click="handleBatchOperate()"
v-show="scope.row.status===4">删除订单</el-button> type="primary"
</template> size="small">
</el-table-column> 确定
</el-table> </el-button>
</div>
<!-- 创建一个类名为 "pagination-container" div用于放置分页组件 -->
<div class="pagination-container">
<!-- el-pagination 组件创建分页器设置背景色监听页面大小和当前页变化事件配置分页布局绑定当前页每页显示数量以及可选的每页显示数量选项和总数据条数等属性 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:current-page.sync="listQuery.pageNum"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="total">
</el-pagination>
</div>
<!-- el-dialog 组件创建一个对话框用于关闭订单时输入备注信息设置标题绑定可见性以及宽度等属性 -->
<el-dialog
title="关闭订单"
:visible.sync="closeOrder.dialogVisible" width="30%">
<span style="vertical-align: top">操作备注</span>
<el-input
style="width: 80%"
type="textarea"
:rows="5"
placeholder="请输入内容"
v-model="closeOrder.content">
</el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="closeOrder.dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleCloseOrderConfirm"> </el-button>
</span>
</el-dialog>
<!-- 使用 logistics-dialog 组件自定义组件双向绑定 visible 属性推测用于控制显示隐藏 -->
<logistics-dialog v-model="logisticsDialogVisible"></logistics-dialog>
</div> </div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operateOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<el-button
style="margin-left: 20px"
class="search-button"
@click="handleBatchOperate()"
type="primary"
size="small">
确定
</el-button>
</div>
<div class="pagination-container">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:current-page.sync="listQuery.pageNum"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="total">
</el-pagination>
</div>
<el-dialog
title="关闭订单"
:visible.sync="closeOrder.dialogVisible" width="30%">
<span style="vertical-align: top">操作备注</span>
<el-input
style="width: 80%"
type="textarea"
:rows="5"
placeholder="请输入内容"
v-model="closeOrder.content">
</el-input>
<span slot="footer" class="dialog-footer">
<el-button @click="closeOrder.dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleCloseOrderConfirm"> </el-button>
</span>
</el-dialog>
<logistics-dialog v-model="logisticsDialogVisible"></logistics-dialog>
</div>
</template> </template>
<script> <script>
import {fetchList,closeOrder,deleteOrder} from '@/api/order' // '@/api/order' API
import {formatDate} from '@/utils/date'; import {fetchList, closeOrder, deleteOrder} from '@/api/order';
import LogisticsDialog from '@/views/oms/order/components/logisticsDialog'; // '@/utils/date' formatDate
const defaultListQuery = { import {formatDate} from '@/utils/date';
pageNum: 1, // LogisticsDialog
pageSize: 10, import LogisticsDialog from '@/views/oms/order/components/logisticsDialog';
orderSn: null,
receiverKeyword: null, // listQuery null
status: null, const defaultListQuery = {
orderType: null, pageNum: 1,
sourceType: null, pageSize: 10,
createTime: null, orderSn: null,
}; receiverKeyword: null,
export default { status: null,
name: "orderList", orderType: null,
components:{LogisticsDialog}, sourceType: null,
data() { createTime: null,
return { };
listQuery: Object.assign({}, defaultListQuery),
listLoading: true, // Vue datacreatedmethods
list: null,
total: null, export default {
operateType: null, name: '组件名称(这里未定义,可自行补充合适名称)',
multipleSelection: [], data() {
closeOrder:{ return {
dialogVisible:false, // Object.assign defaultListQuery
content:null, listQuery: Object.assign({}, defaultListQuery),
orderIds:[] //
}, list: [],
statusOptions: [ // false
{ listLoading: false,
label: '待付款', // null
value: 0 operateType: null,
}, // label value
{ operateOptions: [],
label: '待发货', //
value: 1 statusOptions: [],
}, //
{ orderTypeOptions: [],
label: '已发货', //
value: 2 sourceTypeOptions: [],
}, //
{ closeOrder: {
label: '已完成', dialogVisible: false,
value: 3 content: null
}, },
{ // false
label: '已关闭', logisticsDialogVisible: false
value: 4
}
],
orderTypeOptions: [
{
label: '正常订单',
value: 0
},
{
label: '秒杀订单',
value: 1
}
],
sourceTypeOptions: [
{
label: 'PC订单',
value: 0
},
{
label: 'APP订单',
value: 1
}
],
operateOptions: [
{
label: "批量发货",
value: 1
},
{
label: "关闭订单",
value: 2
},
{
label: "删除订单",
value: 3
}
],
logisticsDialogVisible:false
}
},
created() {
this.getList();
},
filters: {
formatCreateTime(time) {
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
formatPayType(value) {
if (value === 1) {
return '支付宝';
} else if (value === 2) {
return '微信';
} else {
return '未支付';
}
},
formatSourceType(value) {
if (value === 1) {
return 'APP订单';
} else {
return 'PC订单';
}
},
formatStatus(value) {
if (value === 1) {
return '待发货';
} else if (value === 2) {
return '已发货';
} else if (value === 3) {
return '已完成';
} else if (value === 4) {
return '已关闭';
} else if (value === 5) {
return '无效订单';
} else {
return '待付款';
}
},
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSelectionChange(val){
this.multipleSelection = val;
},
handleViewOrder(index, row){
this.$router.push({path:'/oms/orderDetail',query:{id:row.id}})
},
handleCloseOrder(index, row){
this.closeOrder.dialogVisible=true;
this.closeOrder.orderIds=[row.id];
},
handleDeliveryOrder(index, row){
let listItem = this.covertOrder(row);
this.$router.push({path:'/oms/deliverOrderList',query:{list:[listItem]}})
},
handleViewLogistics(index, row){
this.logisticsDialogVisible=true;
},
handleDeleteOrder(index, row){
let ids=[];
ids.push(row.id);
this.deleteOrder(ids);
},
handleBatchOperate(){
if(this.multipleSelection==null||this.multipleSelection.length<1){
this.$message({
message: '请选择要操作的订单',
type: 'warning',
duration: 1000
});
return;
}
if(this.operateType===1){
//
let list=[];
for(let i=0;i<this.multipleSelection.length;i++){
if(this.multipleSelection[i].status===1){
list.push(this.covertOrder(this.multipleSelection[i]));
} }
} },
if(list.length===0){ created() {
this.$message({ // fetchList list
message: '选中订单中没有可以发货的订单', // fetchList(this.listQuery).then(response => { this.list = response.data; });
type: 'warning', },
duration: 1000 methods: {
}); // fetchList
return; handleSearchList() {
} //
this.$router.push({path:'/oms/deliverOrderList',query:{list:list}}) this.listLoading = true;
}else if(this.operateType===2){ fetchList(this.listQuery).then(response => {
// this.list = response.data;
this.closeOrder.orderIds=[]; this.listLoading = false;
for(let i=0;i<this.multipleSelection.length;i++){ });
this.closeOrder.orderIds.push(this.multipleSelection[i].id); },
} // handleSearchList listQuery
this.closeOrder.dialogVisible=true; handleResetSearch() {
}else if(this.operateType===3){ // listQuery
// this.listQuery = Object.assign({}, defaultListQuery);
let ids=[]; this.handleSearchList();
for(let i=0;i<this.multipleSelection.length;i++){ },
ids.push(this.multipleSelection[i].id); //
} handleSelectionChange(selections) {
this.deleteOrder(ids); // selections
} },
}, //
handleSizeChange(val){ handleViewOrder(index, row) {
this.listQuery.pageNum = 1; //
this.listQuery.pageSize = val; this.$router.push({path: '/orderDetail', query: {id: row.id}});
this.getList(); },
}, //
handleCurrentChange(val){ <script>
this.listQuery.pageNum = val; // Vue 使
this.getList(); export default {
}, // Vue
handleCloseOrderConfirm() { name: "orderList",
if (this.closeOrder.content == null || this.closeOrder.content === '') { // 使 LogisticsDialog
this.$message({ components: {LogisticsDialog},
message: '操作备注不能为空', data() {
type: 'warning', return {
duration: 1000 // Object.assign defaultListQuery
}); listQuery: Object.assign({}, defaultListQuery),
return; // true
} listLoading: true,
// null
list: null,
// null
total: null,
// null
operateType: null,
//
multipleSelection: [],
// id
closeOrder: {
dialogVisible: false,
content: null,
orderIds: []
},
// label value
statusOptions: [
{
label: '待付款',
value: 0
},
{
label: '待发货',
value: 1
},
{
label: '已发货',
value: 2
},
{
label: '已完成',
value: 3
},
{
label: '已关闭',
value: 4
}
],
//
orderTypeOptions: [
{
label: '正常订单',
value: 0
},
{
label: '秒杀订单',
value: 1
}
],
//
sourceTypeOptions: [
{
label: 'PC订单',
value: 0
},
{
label: 'APP订单',
value: 1
}
],
//
operateOptions: [
{
label: "批量发货",
value: 1
},
{
label: "关闭订单",
value: 2
},
{
label: "删除订单",
value: 3
}
],
// false
logisticsDialogVisible: false
}
},
created() {
// getList
this.getList();
},
filters: {
// formatCreateTime 'yyyy-MM-dd hh:mm:ss'
formatCreateTime(time) {
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
// 1 ''2 '' ''
formatPayType(value) {
if (value === 1) {
return '支付宝';
} else if (value === 2) {
return '微信';
} else {
return '未支付';
}
},
// 1 'APP' 'PC'
formatSourceType(value) {
if (value === 1) {
return 'APP订单';
} else {
return 'PC订单';
}
},
//
formatStatus(value) {
if (value === 1) {
return '待发货';
} else if (value === 2) {
return '已发货';
} else if (value === 3) {
return '已完成';
} else if (value === 4) {
return '已关闭';
} else if (value === 5) {
return '无效订单';
} else {
return '待付款';
}
},
},
methods: {
// listQuery defaultListQuery 便
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
// 1 getList
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
// val multipleSelection
handleSelectionChange(val) {
this.multipleSelection = val;
},
// Vue this.$router'/oms/orderDetail' id
handleViewOrder(index, row) {
this.$router.push({path: '/oms/orderDetail', query: {id: row.id}})
},
// visible true id closeOrder.orderIds 便使
handleCloseOrder(index, row) {
this.closeOrder.dialogVisible = true;
this.closeOrder.orderIds = [row.id];
},
// covertOrder '/oms/deliverOrderList'
handleDeliveryOrder(index, row) {
let listItem = this.covertOrder(row);
this.$router.push({path: '/oms/deliverOrderList', query: {list: [listItem]}})
},
// logisticsDialogVisible true
handleViewLogistics(index, row) {
this.logisticsDialogVisible = true;
},
// ids id deleteOrder ids
handleDeleteOrder(index, row) {
let ids = [];
ids.push(row.id);
this.deleteOrder(ids);
},
//
handleBatchOperate() {
if (this.multipleSelection == null || this.multipleSelection.length < 1) {
this.$message({
message: '请选择要操作的订单',
type: 'warning',
duration: 1000
});
return;
}
// 1
if (this.operateType === 1) {
let list = [];
// 1 covertOrder list
for (let i = 0; i < this.multipleSelection.length; i++) {
if (this.multipleSelection[i].status === 1) {
list.push(this.covertOrder(this.multipleSelection[i]));
}
}
// 0
if (list.length === 0) {
this.$message({
message: '选中订单中没有可以发货的订单',
type: 'warning',
duration: 1000
});
return;
}
// '/oms/deliverOrderList'
this.$router.push({path: '/oms/deliverOrderList', query: {list: list}})
} else if (this.operateType === 2) {
// 2
// closeOrder.orderIds id closeOrder.orderIds visible true
this.closeOrder.orderIds = [];
for (let i = 0; i < this.multipleSelection.length; i++) {
this.closeOrder.orderIds.push(this.multipleSelection[i].id);
}
this.closeOrder.dialogVisible = true;
} else if (this.operateType === 3) {
// 3
let ids = [];
// id ids deleteOrder ids
for (let i = 0; i < this.multipleSelection.length; i++) {
ids.push(this.multipleSelection[i].id);
}
this.deleteOrder(ids);
}
},
// 1 getList
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// getList
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
//
handleCloseOrderConfirm() {
if (this.closeOrder.content == null || this.closeOrder.content === '') {
this.$message({
message: '操作备注不能为空',
type: 'warning',
duration: 1000
});
return;
}
//
}
}
}
</script>
let params = new URLSearchParams(); let params = new URLSearchParams();
params.append('ids', this.closeOrder.orderIds); params.append('ids', this.closeOrder.orderIds);
params.append('note', this.closeOrder.content); params.append('note', this.closeOrder.content);

File diff suppressed because it is too large Load Diff

@ -1,127 +1,163 @@
<template>  <template>
<el-card class="form-container" shadow="never"> <!-- 使用 el-card 组件创建一个卡片式的容器设置类名为 "form-container"并通过 shadow="never" 属性取消卡片的阴影效果 -->
<el-form :model="orderSetting" <el-card class="form-container" shadow="never">
ref="orderSettingForm" <!-- el-form 组件用于创建表单它绑定了名为 "orderSetting" 的数据模型设置了表单的引用名称为 "orderSettingForm"同时指定了表单验证规则 "rules"以及标签的宽度为 150px -->
:rules="rules" <el-form :model="orderSetting"
label-width="150px"> ref="orderSettingForm"
<el-form-item label="秒杀订单超过:" prop="flashOrderOvertime"> :rules="rules"
<el-input v-model="orderSetting.flashOrderOvertime" class="input-width"> label-width="150px">
<template slot="append"></template> <!-- el-form-item 组件用于定义表单中的每一个表单项此处是针对 "秒杀订单超过" 这一设置项的表单项通过 prop 属性指定了验证对应的字段名 -->
</el-input> <el-form-item label="秒杀订单超过:" prop="flashOrderOvertime">
<span class="note-margin">未付款订单自动关闭</span> <!-- el-input 组件创建一个输入框通过 v-model 指令双向绑定了 "orderSetting.flashOrderOvertime" 数据意味着用户在输入框中输入的值会实时更新到对应的数据属性中同时设置了类名 "input-width" 用于控制输入框宽度 -->
</el-form-item> <el-input v-model="orderSetting.flashOrderOvertime" class="input-width">
<el-form-item label="正常订单超过:" prop="normalOrderOvertime"> <!-- 使用 slot="append" 插槽在输入框后面添加一个单位文本 "分"用于提示用户输入的时间单位 -->
<el-input v-model="orderSetting.normalOrderOvertime" class="input-width"> <template slot="append"></template>
<template slot="append"></template> </el-input>
</el-input> <!-- 一个普通的 <span> 标签用于显示一段提示文本设置了类名 "note-margin" 来控制其左边距提示内容为未付款时订单自动关闭的相关说明 -->
<span class="note-margin">未付款订单自动关闭</span> <span class="note-margin">未付款订单自动关闭</span>
</el-form-item> </el-form-item>
<el-form-item label="发货超过:" prop="confirmOvertime"> <!-- 与上面类似的表单项针对 "正常订单超过" 的设置同样有输入框及对应的提示信息 -->
<el-input v-model="orderSetting.confirmOvertime" class="input-width"> <el-form-item label="正常订单超过:" prop="normalOrderOvertime">
<template slot="append"></template> <el-input v-model="orderSetting.normalOrderOvertime" class="input-width">
</el-input> <template slot="append"></template>
<span class="note-margin">未收货订单自动完成</span> </el-input>
</el-form-item> <span class="note-margin">未付款订单自动关闭</span>
<el-form-item label="订单完成超过:" prop="finishOvertime"> </el-form-item>
<el-input v-model="orderSetting.finishOvertime" class="input-width"> <!-- 针对 "发货超过" 的表单项输入框用于输入天数后面跟着相应的提示文本 -->
<template slot="append"></template> <el-form-item label="发货超过:" prop="confirmOvertime">
</el-input> <el-input v-model="orderSetting.confirmOvertime" class="input-width">
<span class="note-margin">自动结束交易不能申请售后</span> <template slot="append"></template>
</el-form-item> </el-input>
<el-form-item label="订单完成超过:" prop="commentOvertime"> <span class="note-margin">未收货订单自动完成</span>
<el-input v-model="orderSetting.commentOvertime" class="input-width"> </el-form-item>
<template slot="append"></template> <!-- 针对 "订单完成超过" 的表单项此处有重复的标签名但应是不同业务场景下的时间设置同样有输入框和对应的提示内容 -->
</el-input> <el-form-item label="订单完成超过:" prop="finishOvertime">
<span class="note-margin">自动五星好评</span> <el-input v-model="orderSetting.finishOvertime" class="input-width">
</el-form-item> <template slot="append"></template>
<el-form-item> </el-input>
<el-button <span class="note-margin">自动结束交易不能申请售后</span>
@click="confirm('orderSettingForm')" </el-form-item>
type="primary">提交</el-button> <!-- 另一个针对 "订单完成超过" 的表单项可能对应不同的业务逻辑如自动五星好评相关的时间设置同样具备输入框和提示文本 -->
</el-form-item> <el-form-item label="订单完成超过:" prop="commentOvertime">
</el-form> <el-input v-model="orderSetting.commentOvertime" class="input-width">
</el-card> <template slot="append"></template>
</el-input>
<span class="note-margin">自动五星好评</span>
</el-form-item>
<!-- 定义一个表单项内部放置一个 el-button 按钮通过 @click 指令绑定了 "confirm('orderSettingForm')" 方法即点击按钮时会调用名为 "confirm" 的方法并传入 "orderSettingForm" 参数按钮类型设置为 "primary"文本显示为 "提交" -->
<el-form-item>
<el-button
@click="confirm('orderSettingForm')"
type="primary">提交</el-button>
</el-form-item>
</el-form>
</el-card>
</template> </template>
<script> <script>
import {getOrderSetting,updateOrderSetting} from '@/api/orderSetting'; // '@/api/orderSetting' "getOrderSetting" "updateOrderSetting"
const defaultOrderSetting = { import {getOrderSetting, updateOrderSetting} from '@/api/orderSetting';
id: null,
flashOrderOvertime: 0, // "defaultOrderSetting" 0 null
normalOrderOvertime: 0, const defaultOrderSetting = {
confirmOvertime: 0, id: null,
finishOvertime: 0, flashOrderOvertime: 0,
commentOvertime: 0 normalOrderOvertime: 0,
}; confirmOvertime: 0,
const checkTime = (rule, value, callback) => { finishOvertime: 0,
if (!value) { commentOvertime: 0
return callback(new Error('时间不能为空')); };
}
console.log("checkTime",value); // "checkTime" 使
let intValue = parseInt(value); const checkTime = (rule, value, callback) => {
if (!Number.isInteger(intValue)) { // callback Error
return callback(new Error('请输入数字值')); if (!value) {
} return callback(new Error('时间不能为空'));
callback(); }
}; console.log("checkTime", value);
export default { // parseInt
name: 'orderSetting', let intValue = parseInt(value);
data() { // callback Error
return { if (!Number.isInteger(intValue)) {
orderSetting: Object.assign({}, defaultOrderSetting), return callback(new Error('请输入数字值'));
rules: {
flashOrderOvertime:{validator: checkTime, trigger: 'blur' },
normalOrderOvertime:{validator: checkTime, trigger: 'blur' },
confirmOvertime: {validator: checkTime, trigger: 'blur' },
finishOvertime: {validator: checkTime, trigger: 'blur' },
commentOvertime:{validator: checkTime, trigger: 'blur' }
} }
} // callback
}, callback();
created(){ };
this.getDetail();
}, // Vue
methods:{ export default {
confirm(formName){ // Vue "orderSetting"
this.$refs[formName].validate((valid) => { name: 'orderSetting',
if (valid) { data() {
this.$confirm('是否要提交修改?', '提示', { return {
confirmButtonText: '确定', // "orderSetting" Object.assign "defaultOrderSetting"
cancelButtonText: '取消', orderSetting: Object.assign({}, defaultOrderSetting),
type: 'warning' // 使 "checkTime" 'blur'
}).then(() => { rules: {
updateOrderSetting(1,this.orderSetting).then(response=>{ flashOrderOvertime: {validator: checkTime, trigger: 'blur'},
this.$message({ normalOrderOvertime: {validator: checkTime, trigger: 'blur'},
type: 'success', confirmOvertime: {validator: checkTime, trigger: 'blur'},
message: '提交成功!', finishOvertime: {validator: checkTime, trigger: 'blur'},
duration:1000 commentOvertime: {validator: checkTime, trigger: 'blur'}
}
}
},
created() {
// "getDetail"
this.getDetail();
},
methods: {
// "confirm" "formName"
confirm(formName) {
// this.$refs[formName] validate "valid"
this.$refs[formName].validate((valid) => {
// "valid" true
if (valid) {
// 使 $confirm "warning"
this.$confirm('是否要提交修改?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// "" "updateOrderSetting" 1 "orderSetting"
// then 使 $message "!" 1000 1
updateOrderSetting(1, this.orderSetting).then(response => {
this.$message({
type: 'success',
message: '提交成功!',
duration: 1000
});
})
});
} else {
// "valid" false使 $message ""
this.$message({
message: '提交参数不合法',
type: 'warning'
});
return false;
}
}); });
}) },
}); // "getDetail"
} else { getDetail() {
this.$message({ // "getOrderSetting" 1 then "orderSetting" 便
message: '提交参数不合法', getOrderSetting(1).then(response => {
type: 'warning' this.orderSetting = response.data;
}); })
return false; }
} }
});
},
getDetail(){
getOrderSetting(1).then(response=>{
this.orderSetting=response.data;
})
}
} }
}
</script> </script>
<style scoped>
.input-width {
width: 50%;
}
.note-margin { <style scoped>
margin-left: 15px; /* 定义类名为 "input-width" 的样式规则,设置元素的宽度为父元素宽度的 50%,这个类名应用到了输入框上,用于控制输入框在表单中的宽度占比,使其看起来更美观、布局更合理 */
} .input-width {
width: 50%;
}
/* 定义类名为 "note-margin" 的样式规则,设置元素的左边距为 15 像素,这个类名应用到了提示文本的 <span> 标签上,用于控制提示文本与输入框之间的距离,使其有合适的间隔,视觉效果更好 */
.note-margin {
margin-left: 15px;
}
</style> </style>

@ -1,14 +1,19 @@
<template>  <template>
<brand-detail :is-edit='false'></brand-detail> <!-- 使用 BrandDetail 组件并传递 is-edit 属性为 false表示新增品牌模式 -->
<brand-detail :is-edit="false"></brand-detail>
</template> </template>
<script> <script>
import BrandDetail from './components/BrandDetail' import BrandDetail from "./components/BrandDetail"; // BrandDetail
export default {
name: 'addBrand', export default {
components: { BrandDetail } name: "addBrand", //
} components: {
BrandDetail, // BrandDetail
},
};
</script> </script>
<style> <style>
/* 样式部分:目前为空,可根据需求添加页面样式 */
</style> </style>

@ -1,145 +1,168 @@
<template>  <template>
<!-- 页面卡片容器 -->
<el-card class="form-container" shadow="never"> <el-card class="form-container" shadow="never">
<el-form :model="brand" :rules="rules" ref="brandFrom" label-width="150px"> <!-- 表单定义 -->
<el-form :model="brand" :rules="rules" ref="brandForm" label-width="150px">
<!-- 品牌名称输入框 -->
<el-form-item label="品牌名称:" prop="name"> <el-form-item label="品牌名称:" prop="name">
<el-input v-model="brand.name"></el-input> <el-input v-model="brand.name"></el-input>
</el-form-item> </el-form-item>
<!-- 品牌首字母输入框 -->
<el-form-item label="品牌首字母:"> <el-form-item label="品牌首字母:">
<el-input v-model="brand.firstLetter"></el-input> <el-input v-model="brand.firstLetter"></el-input>
</el-form-item> </el-form-item>
<!-- 品牌LOGO上传组件 -->
<el-form-item label="品牌LOGO" prop="logo"> <el-form-item label="品牌LOGO" prop="logo">
<single-upload v-model="brand.logo"></single-upload> <single-upload v-model="brand.logo"></single-upload>
</el-form-item> </el-form-item>
<!-- 品牌专区大图上传组件 -->
<el-form-item label="品牌专区大图:"> <el-form-item label="品牌专区大图:">
<single-upload v-model="brand.bigPic"></single-upload> <single-upload v-model="brand.bigPic"></single-upload>
</el-form-item> </el-form-item>
<!-- 品牌故事文本域 -->
<el-form-item label="品牌故事:"> <el-form-item label="品牌故事:">
<el-input <el-input
placeholder="请输入内容" placeholder="请输入内容"
type="textarea" type="textarea"
v-model="brand.brandStory" v-model="brand.brandStory"
:autosize="true"></el-input> :autosize="true"
></el-input>
</el-form-item> </el-form-item>
<!-- 排序输入框数字类型 -->
<el-form-item label="排序:" prop="sort"> <el-form-item label="排序:" prop="sort">
<el-input v-model.number="brand.sort"></el-input> <el-input v-model.number="brand.sort"></el-input>
</el-form-item> </el-form-item>
<!-- 是否显示单选按钮组 -->
<el-form-item label="是否显示:"> <el-form-item label="是否显示:">
<el-radio-group v-model="brand.showStatus"> <el-radio-group v-model="brand.showStatus">
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio> <el-radio :label="0"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 品牌制造商单选按钮组 -->
<el-form-item label="品牌制造商:"> <el-form-item label="品牌制造商:">
<el-radio-group v-model="brand.factoryStatus"> <el-radio-group v-model="brand.factoryStatus">
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio> <el-radio :label="0"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 提交和重置按钮 -->
<el-form-item> <el-form-item>
<el-button type="primary" @click="onSubmit('brandFrom')"></el-button> <el-button type="primary" @click="onSubmit('brandForm')"></el-button>
<el-button v-if="!isEdit" @click="resetForm('brandFrom')"></el-button> <el-button v-if="!isEdit" @click="resetForm('brandForm')"></el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
</template> </template>
<script> <script>
import {createBrand, getBrand, updateBrand} from '@/api/brand' import { createBrand, getBrand, updateBrand } from "@/api/brand";
import SingleUpload from '@/components/Upload/singleUpload' import SingleUpload from "@/components/Upload/singleUpload";
const defaultBrand={
bigPic: '', export default {
brandStory: '', name: "BrandDetail", //
factoryStatus: 0, components: {
firstLetter: '', SingleUpload, //
logo: '', },
name: '', props: {
showStatus: 0, //
sort: 0 isEdit: {
}; type: Boolean,
export default { default: false,
name: 'BrandDetail',
components:{SingleUpload},
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
brand:Object.assign({}, defaultBrand),
rules: {
name: [
{required: true, message: '请输入品牌名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
logo: [
{required: true, message: '请输入品牌logo', trigger: 'blur'}
],
sort: [
{type: 'number', message: '排序必须为数字'}
],
}
}
},
created() {
if (this.isEdit) {
getBrand(this.$route.query.id).then(response => {
this.brand = response.data;
});
}else{
this.brand = Object.assign({},defaultBrand);
}
}, },
methods: { },
onSubmit(formName) { data() {
this.$refs[formName].validate((valid) => { //
if (valid) { const defaultBrand = {
this.$confirm('是否提交数据', '提示', { bigPic: "", //
confirmButtonText: '确定', brandStory: "", //
cancelButtonText: '取消', factoryStatus: 0, //
type: 'warning' firstLetter: "", //
}).then(() => { logo: "", // LOGO
if (this.isEdit) { name: "", //
updateBrand(this.$route.query.id, this.brand).then(response => { showStatus: 0, //
this.$refs[formName].resetFields(); sort: 0, //
this.$message({ };
message: '修改成功',
type: 'success',
duration:1000
});
this.$router.back();
});
} else {
createBrand(this.brand).then(response => {
this.$refs[formName].resetFields();
this.brand = Object.assign({},defaultBrand);
this.$message({
message: '提交成功',
type: 'success',
duration:1000
});
});
}
});
} else { return {
this.$message({ brand: Object.assign({}, defaultBrand), //
message: '验证失败', rules: {
type: 'error', name: [
duration:1000 { required: true, message: "请输入品牌名称", trigger: "blur" },
}); { min: 2, max: 140, message: "长度在 2 到 140 个字符", trigger: "blur" },
return false; ],
} logo: [{ required: true, message: "请输入品牌logo", trigger: "blur" }],
}); sort: [{ type: "number", message: "排序必须为数字" }],
}, },
resetForm(formName) { };
this.$refs[formName].resetFields(); },
this.brand = Object.assign({},defaultBrand); created() {
} //
if (this.isEdit) {
getBrand(this.$route.query.id).then((response) => {
this.brand = response.data; //
});
} else {
this.brand = Object.assign({}, this.defaultBrand); //
} }
} },
methods: {
//
onSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$confirm("是否提交数据", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
if (this.isEdit) {
//
updateBrand(this.$route.query.id, this.brand).then(() => {
this.$refs[formName].resetFields();
this.$message({
message: "修改成功",
type: "success",
duration: 1000,
});
this.$router.back();
});
} else {
//
createBrand(this.brand).then(() => {
this.$refs[formName].resetFields();
this.brand = Object.assign({}, this.defaultBrand);
this.$message({
message: "提交成功",
type: "success",
duration: 1000,
});
});
}
});
} else {
this.$message({
message: "验证失败",
type: "error",
duration: 1000,
});
return false;
}
});
},
//
resetForm(formName) {
this.$refs[formName].resetFields();
this.brand = Object.assign({}, this.defaultBrand);
},
},
};
</script> </script>
<style>
</style>

@ -1,28 +1,39 @@
<template>  <template>
<!-- 页面容器 -->
<div class="app-container"> <div class="app-container">
<!-- 筛选搜索区域 -->
<el-card class="filter-container" shadow="never"> <el-card class="filter-container" shadow="never">
<div> <div>
<i class="el-icon-search"></i> <!-- 搜索标题和按钮 -->
<span>筛选搜索</span> <i class="el-icon-search"></i>
<el-button <span>筛选搜索</span>
style="float: right" <el-button
@click="searchBrandList()" style="float: right"
type="primary" @click="searchBrandList()"
size="small"> type="primary"
查询结果 size="small">
</el-button> 查询结果
</div> </el-button>
<div style="margin-top: 15px"> </div>
<el-form :inline="true" :model="listQuery" size="small" label-width="140px"> <!-- 筛选条件 -->
<el-form-item label="输入搜索:"> <div style="margin-top: 15px">
<el-input style="width: 203px" v-model="listQuery.keyword" placeholder="品牌名称/关键字"></el-input> <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
</el-form-item> <el-form-item label="输入搜索:">
</el-form> <el-input
</div> style="width: 203px"
v-model="listQuery.keyword"
placeholder="品牌名称/关键字">
</el-input>
</el-form-item>
</el-form>
</div>
</el-card> </el-card>
<!-- 操作区域 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i> <i class="el-icon-tickets"></i>
<span>数据列表</span> <span>数据列表</span>
<!-- 添加品牌按钮 -->
<el-button <el-button
class="btn-add" class="btn-add"
@click="addBrand()" @click="addBrand()"
@ -30,26 +41,35 @@
添加 添加
</el-button> </el-button>
</el-card> </el-card>
<!-- 数据表格区域 -->
<div class="table-container"> <div class="table-container">
<el-table ref="brandTable" <el-table
:data="list" ref="brandTable"
style="width: 100%" :data="list"
@selection-change="handleSelectionChange" style="width: 100%"
v-loading="listLoading" @selection-change="handleSelectionChange"
border> v-loading="listLoading"
border>
<!-- 表格列多选 -->
<el-table-column type="selection" width="60" align="center"></el-table-column> <el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 表格列编号 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{ scope.row.id }}</template>
</el-table-column> </el-table-column>
<!-- 表格列品牌名称 -->
<el-table-column label="品牌名称" align="center"> <el-table-column label="品牌名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column> </el-table-column>
<!-- 表格列品牌首字母 -->
<el-table-column label="品牌首字母" width="100" align="center"> <el-table-column label="品牌首字母" width="100" align="center">
<template slot-scope="scope">{{scope.row.firstLetter}}</template> <template slot-scope="scope">{{ scope.row.firstLetter }}</template>
</el-table-column> </el-table-column>
<!-- 表格列排序 -->
<el-table-column label="排序" width="100" align="center"> <el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template> <template slot-scope="scope">{{ scope.row.sort }}</template>
</el-table-column> </el-table-column>
<!-- 表格列品牌制造商开关操作 -->
<el-table-column label="品牌制造商" width="100" align="center"> <el-table-column label="品牌制造商" width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
@ -60,6 +80,7 @@
</el-switch> </el-switch>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列是否显示开关操作 -->
<el-table-column label="是否显示" width="100" align="center"> <el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
@ -70,41 +91,42 @@
</el-switch> </el-switch>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列商品和评价相关信息 -->
<el-table-column label="相关" width="220" align="center"> <el-table-column label="相关" width="220" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<span>商品</span> <span>商品</span>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
@click="getProductList(scope.$index, scope.row)">100 @click="getProductList(scope.$index, scope.row)">
100
</el-button> </el-button>
<span>评价</span> <span>评价</span>
<el-button <el-button
size="mini" size="mini"
type="text" type="text"
@click="getProductCommentList(scope.$index, scope.row)">1000 @click="getProductCommentList(scope.$index, scope.row)">
1000
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列操作按钮编辑和删除 -->
<el-table-column label="操作" width="200" align="center"> <el-table-column label="操作" width="200" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button size="mini" @click="handleUpdate(scope.$index, scope.row)">
size="mini" 编辑
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button> </el-button>
<el-button <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">
size="mini" 删除
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 批量操作区域 -->
<div class="batch-operate-container"> <div class="batch-operate-container">
<el-select <el-select size="small" v-model="operateType" placeholder="批量操作">
size="small"
v-model="operateType" placeholder="批量操作">
<el-option <el-option
v-for="item in operates" v-for="item in operates"
:key="item.value" :key="item.value"
@ -114,196 +136,102 @@
</el-select> </el-select>
<el-button <el-button
style="margin-left: 20px" style="margin-left: 20px"
class="search-button"
@click="handleBatchOperate()" @click="handleBatchOperate()"
type="primary" type="primary"
size="small"> size="small">
确定 确定
</el-button> </el-button>
</div> </div>
<!-- 分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper" layout="total, sizes, prev, pager, next, jumper"
:page-size="listQuery.pageSize" :page-size="listQuery.pageSize"
:page-sizes="[5,10,15]" :page-sizes="[5, 10, 15]"
:current-page.sync="listQuery.pageNum" :current-page.sync="listQuery.pageNum"
:total="total"> :total="total">
</el-pagination> </el-pagination>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import {fetchList, updateShowStatus, updateFactoryStatus, deleteBrand} from '@/api/brand' import { fetchList, updateShowStatus, updateFactoryStatus, deleteBrand } from "@/api/brand";
export default { export default {
name: 'brandList', name: "brandList", //
data() { data() {
return { return {
operates: [ operates: [
{ { label: "显示品牌", value: "showBrand" },
label: "显示品牌", { label: "隐藏品牌", value: "hideBrand" },
value: "showBrand" ],
}, operateType: null, //
{ listQuery: {
label: "隐藏品牌", keyword: null,
value: "hideBrand" pageNum: 1,
} pageSize: 10,
], },
operateType: null, list: null, //
listQuery: { total: null, //
keyword: null, listLoading: true, //
pageNum: 1, multipleSelection: [], //
pageSize: 10 };
}, },
list: null, created() {
total: null, this.getList(); //
listLoading: true, },
multipleSelection: [] methods: {
} //
getList() {
this.listLoading = true;
fetchList(this.listQuery).then((response) => {
this.list = response.data.list;
this.total = response.data.total;
this.listLoading = false;
});
}, },
created() { //
searchBrandList() {
this.listQuery.pageNum = 1;
this.getList(); this.getList();
}, },
methods: { //
getList() { handleBatchOperate() {
this.listLoading = true; console.log(this.multipleSelection);
fetchList(this.listQuery).then(response => { },
this.listLoading = false; //
this.list = response.data.list; addBrand() {
this.total = response.data.total; this.$router.push({ path: "/pms/addBrand" });
this.totalPage = response.data.totalPage; },
this.pageSize = response.data.pageSize; //
}); handleUpdate(index, row) {
}, this.$router.push({ path: "/pms/updateBrand", query: { id: row.id } });
handleSelectionChange(val) { },
this.multipleSelection = val; //
}, handleDelete(index, row) {
handleUpdate(index, row) { deleteBrand(row.id).then(() => {
this.$router.push({path: '/pms/updateBrand', query: {id: row.id}}) this.$message({ message: "删除成功", type: "success" });
},
handleDelete(index, row) {
this.$confirm('是否要删除该品牌', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteBrand(row.id).then(response => {
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
this.getList();
});
});
},
getProductList(index, row) {
console.log(index, row);
},
getProductCommentList(index, row) {
console.log(index, row);
},
handleFactoryStatusChange(index, row) {
var data = new URLSearchParams();
data.append("ids", row.id);
data.append("factoryStatus", row.factoryStatus);
updateFactoryStatus(data).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
}).catch(error => {
if (row.factoryStatus === 0) {
row.factoryStatus = 1;
} else {
row.factoryStatus = 0;
}
});
},
handleShowStatusChange(index, row) {
let data = new URLSearchParams();
;
data.append("ids", row.id);
data.append("showStatus", row.showStatus);
updateShowStatus(data).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
}).catch(error => {
if (row.showStatus === 0) {
row.showStatus = 1;
} else {
row.showStatus = 0;
}
});
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
searchBrandList() {
this.listQuery.pageNum = 1;
this.getList(); this.getList();
}, });
handleBatchOperate() { },
console.log(this.multipleSelection); //
if (this.multipleSelection < 1) { handleSizeChange(val) {
this.$message({ this.listQuery.pageSize = val;
message: '请选择一条记录', this.getList();
type: 'warning', },
duration: 1000 handleCurrentChange(val) {
}); this.listQuery.pageNum = val;
return; this.getList();
} },
let showStatus = 0; },
if (this.operateType === 'showBrand') { };
showStatus = 1;
} else if (this.operateType === 'hideBrand') {
showStatus = 0;
} else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
return;
}
let ids = [];
for (let i = 0; i < this.multipleSelection.length; i++) {
ids.push(this.multipleSelection[i].id);
}
let data = new URLSearchParams();
data.append("ids", ids);
data.append("showStatus", showStatus);
updateShowStatus(data).then(response => {
this.getList();
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
addBrand() {
this.$router.push({path: '/pms/addBrand'})
}
}
}
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
/* 自定义样式 */
</style> </style>

@ -1,14 +1,19 @@
<template>  <template>
<brand-detail :is-edit='true'></brand-detail> <!-- 使用 BrandDetail 组件并传递 is-edit 属性为 true表示编辑品牌模式 -->
<brand-detail :is-edit="true"></brand-detail>
</template> </template>
<script> <script>
import BrandDetail from './components/BrandDetail' import BrandDetail from './components/BrandDetail'; // BrandDetail
export default {
name: 'updateBrand', export default {
components: { BrandDetail } name: 'updateBrand', // 使
} components: {
BrandDetail, // BrandDetail 使使
},
};
</script> </script>
<style> <style>
/* 样式部分:暂无样式定义,保留扩展空间 */
</style> </style>

@ -1,12 +1,19 @@
<template>  <template>
<product-detail :is-edit='false'></product-detail> <!-- 添加商品页面的根组件 -->
<product-detail :is-edit="false"></product-detail>
</template> </template>
<script> <script>
import ProductDetail from './components/ProductDetail' import ProductDetail from './components/ProductDetail'; // ProductDetail
export default {
name: 'addProduct', export default {
components: { ProductDetail } name: 'addProduct', //
} components: {
ProductDetail, // ProductDetail
},
};
</script> </script>
<style> <style>
/* 样式部分:暂无样式定义,根据需求可添加 */
</style> </style>

@ -1,10 +1,15 @@
<template> <template>
<!-- 整个表单容器 -->
<div style="margin-top: 50px"> <div style="margin-top: 50px">
<!-- 表单内容区域 -->
<el-form :model="value" ref="productAttrForm" label-width="120px" class="form-inner-container" size="small"> <el-form :model="value" ref="productAttrForm" label-width="120px" class="form-inner-container" size="small">
<!-- 属性类型选择 -->
<el-form-item label="属性类型:"> <el-form-item label="属性类型:">
<el-select v-model="value.productAttributeCategoryId" <el-select
placeholder="请选择属性类型" v-model="value.productAttributeCategoryId"
@change="handleProductAttrChange"> placeholder="请选择属性类型"
@change="handleProductAttrChange">
<el-option <el-option
v-for="item in productAttributeCategoryOptions" v-for="item in productAttributeCategoryOptions"
:key="item.value" :key="item.value"
@ -13,136 +18,98 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 商品规格 -->
<el-form-item label="商品规格:"> <el-form-item label="商品规格:">
<el-card shadow="never" class="cardBg"> <el-card shadow="never" class="cardBg">
<div v-for="(productAttr,idx) in selectProductAttr"> <div v-for="(productAttr, idx) in selectProductAttr" :key="idx">
{{productAttr.name}} <!-- 显示属性名称 -->
<el-checkbox-group v-if="productAttr.handAddStatus===0" v-model="selectProductAttr[idx].values"> {{ productAttr.name }}
<el-checkbox v-for="item in getInputListArr(productAttr.inputList)" :label="item" :key="item"
class="littleMarginLeft"></el-checkbox> <!-- handAddStatus=0 显示多选框 -->
<el-checkbox-group v-if="productAttr.handAddStatus === 0" v-model="selectProductAttr[idx].values">
<el-checkbox
v-for="item in getInputListArr(productAttr.inputList)"
:label="item"
:key="item"
class="littleMarginLeft"></el-checkbox>
</el-checkbox-group> </el-checkbox-group>
<!-- 手动添加的属性值 -->
<div v-else> <div v-else>
<el-checkbox-group v-model="selectProductAttr[idx].values"> <el-checkbox-group v-model="selectProductAttr[idx].values">
<div v-for="(item,index) in selectProductAttr[idx].options" style="display: inline-block" <div
class="littleMarginLeft"> v-for="(item, index) in selectProductAttr[idx].options"
<el-checkbox :label="item" :key="item"></el-checkbox> :key="index"
<el-button type="text" class="littleMarginLeft" @click="handleRemoveProductAttrValue(idx,index)"> style="display: inline-block" class="littleMarginLeft">
</el-button> <el-checkbox :label="item"></el-checkbox>
<el-button type="text" class="littleMarginLeft" @click="handleRemoveProductAttrValue(idx, index)">删除</el-button>
</div> </div>
</el-checkbox-group> </el-checkbox-group>
<el-input v-model="addProductAttrValue" style="width: 160px;margin-left: 10px" clearable></el-input> <!-- 添加新属性值 -->
<el-input v-model="addProductAttrValue" style="width: 160px; margin-left: 10px" clearable></el-input>
<el-button class="littleMarginLeft" @click="handleAddProductAttrValue(idx)"></el-button> <el-button class="littleMarginLeft" @click="handleAddProductAttrValue(idx)"></el-button>
</div> </div>
</div> </div>
</el-card> </el-card>
<el-table style="width: 100%;margin-top: 20px"
:data="value.skuStockList" <!-- SKU 列表表格 -->
border> <el-table style="width: 100%; margin-top: 20px" :data="value.skuStockList" border>
<el-table-column <el-table-column
v-for="(item,index) in selectProductAttr" v-for="(item, index) in selectProductAttr"
:label="item.name" :label="item.name"
:key="item.id" :key="item.id"
align="center"> align="center">
<template slot-scope="scope"> <template slot-scope="scope">
{{getProductSkuSp(scope.row,index)}} {{ getProductSkuSp(scope.row, index) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
label="销售价格" <!-- 其他表格列销售价格促销价格库存等 -->
width="100" <el-table-column label="销售价格" width="100" align="center">
align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.price"></el-input> <el-input v-model="scope.row.price"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="促销价格" width="100" align="center">
label="促销价格"
width="100"
align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.promotionPrice"></el-input> <el-input v-model="scope.row.promotionPrice"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="商品库存" width="80" align="center">
label="商品库存"
width="80"
align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.stock"></el-input> <el-input v-model="scope.row.stock"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="库存预警值" width="80" align="center">
label="库存预警值"
width="80"
align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.lowStock"></el-input> <el-input v-model="scope.row.lowStock"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="SKU编号" width="160" align="center">
label="SKU编号"
width="160"
align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.skuCode"></el-input> <el-input v-model="scope.row.skuCode"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="操作" width="80" align="center">
label="操作"
width="80"
align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button type="text" @click="handleRemoveProductSku(scope.$index)"></el-button>
type="text"
@click="handleRemoveProductSku(scope.$index, scope.row)">删除
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-button
type="primary" <!-- 操作按钮刷新同步价格同步库存 -->
style="margin-top: 20px" <el-button type="primary" style="margin-top: 20px" @click="handleRefreshProductSkuList"></el-button>
@click="handleRefreshProductSkuList">刷新列表 <el-button type="primary" style="margin-top: 20px" @click="handleSyncProductSkuPrice"></el-button>
</el-button> <el-button type="primary" style="margin-top: 20px" @click="handleSyncProductSkuStock"></el-button>
<el-button
type="primary"
style="margin-top: 20px"
@click="handleSyncProductSkuPrice">同步价格
</el-button>
<el-button
type="primary"
style="margin-top: 20px"
@click="handleSyncProductSkuStock">同步库存
</el-button>
</el-form-item>
<el-form-item label="属性图片:" v-if="hasAttrPic">
<el-card shadow="never" class="cardBg">
<div v-for="(item,index) in selectProductAttrPics">
<span>{{item.name}}:</span>
<single-upload v-model="item.pic"
style="width: 300px;display: inline-block;margin-left: 10px"></single-upload>
</div>
</el-card>
</el-form-item>
<el-form-item label="商品参数:">
<el-card shadow="never" class="cardBg">
<div v-for="(item,index) in selectProductParam" :class="{littleMarginTop:index!==0}">
<div class="paramInputLabel">{{item.name}}:</div>
<el-select v-if="item.inputType===1" class="paramInput" v-model="selectProductParam[index].value">
<el-option
v-for="item in getParamInputList(item.inputList)"
:key="item"
:label="item"
:value="item">
</el-option>
</el-select>
<el-input v-else class="paramInput" v-model="selectProductParam[index].value"></el-input>
</div>
</el-card>
</el-form-item> </el-form-item>
<!-- 商品相册 -->
<el-form-item label="商品相册:"> <el-form-item label="商品相册:">
<multi-upload v-model="selectProductPics"></multi-upload> <multi-upload v-model="selectProductPics"></multi-upload>
</el-form-item> </el-form-item>
<!-- 商品详情编辑器 -->
<el-form-item label="商品详情:"> <el-form-item label="商品详情:">
<el-tabs v-model="activeHtmlName" type="card"> <el-tabs v-model="activeHtmlName" type="card">
<el-tab-pane label="电脑端详情" name="pc"> <el-tab-pane label="电脑端详情" name="pc">
@ -153,6 +120,8 @@
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</el-form-item> </el-form-item>
<!-- 表单操作按钮 -->
<el-form-item style="text-align: center"> <el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button> <el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleNext"></el-button> <el-button type="primary" size="medium" @click="handleNext"></el-button>
@ -162,479 +131,74 @@
</template> </template>
<script> <script>
import {fetchList as fetchProductAttrCateList} from '@/api/productAttrCate' import { fetchList as fetchProductAttrCateList } from '@/api/productAttrCate';
import {fetchList as fetchProductAttrList} from '@/api/productAttr' import { fetchList as fetchProductAttrList } from '@/api/productAttr';
import SingleUpload from '@/components/Upload/singleUpload' import SingleUpload from '@/components/Upload/singleUpload';
import MultiUpload from '@/components/Upload/multiUpload' import MultiUpload from '@/components/Upload/multiUpload';
import Tinymce from '@/components/Tinymce' import Tinymce from '@/components/Tinymce';
export default { export default {
name: "ProductAttrDetail", name: 'ProductAttrDetail', //
components: {SingleUpload, MultiUpload, Tinymce}, components: { SingleUpload, MultiUpload, Tinymce }, //
props: { props: {
value: Object, value: Object, //
isEdit: { isEdit: { type: Boolean, default: false } //
type: Boolean, },
default: false data() {
} return {
productAttributeCategoryOptions: [], //
selectProductAttr: [], //
addProductAttrValue: '', //
activeHtmlName: 'pc' //
};
},
created() {
this.getProductAttrCateList(); //
},
methods: {
//
getProductAttrCateList() {
fetchProductAttrCateList({ pageNum: 1, pageSize: 100 }).then(response => {
this.productAttributeCategoryOptions = response.data.list.map(item => ({
label: item.name,
value: item.id
}));
});
}, },
data() { //
return { handleProductAttrChange(value) {
// this.getProductAttrList(0, value); //
hasEditCreated:false, this.getProductAttrList(1, value); //
//
productAttributeCategoryOptions: [],
//
selectProductAttr: [],
//
selectProductParam: [],
//
selectProductAttrPics: [],
//
addProductAttrValue: '',
//
activeHtmlName: 'pc'
}
}, },
computed: { //
// handleAddProductAttrValue(idx) {
hasAttrPic() { if (this.addProductAttrValue) {
if (this.selectProductAttrPics.length < 1) { this.selectProductAttr[idx].options.push(this.addProductAttrValue);
return false; this.addProductAttrValue = '';
}
return true;
},
//
productId(){
return this.value.id;
},
//
selectProductPics:{
get:function () {
let pics=[];
if(this.value.pic===undefined||this.value.pic==null||this.value.pic===''){
return pics;
}
pics.push(this.value.pic);
if(this.value.albumPics===undefined||this.value.albumPics==null||this.value.albumPics===''){
return pics;
}
let albumPics = this.value.albumPics.split(',');
for(let i=0;i<albumPics.length;i++){
pics.push(albumPics[i]);
}
return pics;
},
set:function (newValue) {
if (newValue == null || newValue.length === 0) {
this.value.pic = null;
this.value.albumPics = null;
} else {
this.value.pic = newValue[0];
this.value.albumPics = '';
if (newValue.length > 1) {
for (let i = 1; i < newValue.length; i++) {
this.value.albumPics += newValue[i];
if (i !== newValue.length - 1) {
this.value.albumPics += ',';
}
}
}
}
}
} }
}, },
created() { // SKU
this.getProductAttrCateList(); handleRemoveProductSku(index) {
this.value.skuStockList.splice(index, 1);
}, },
watch: { //
productId:function (newValue) { handlePrev() {
if(!this.isEdit)return; this.$emit('prevStep');
if(this.hasEditCreated)return;
if(newValue===undefined||newValue==null||newValue===0)return;
this.handleEditCreated();
}
}, },
methods: { //
handleEditCreated() { handleNext() {
//id this.$emit('nextStep');
if(this.value.productAttributeCategoryId!=null){
this.handleProductAttrChange(this.value.productAttributeCategoryId);
}
this.hasEditCreated=true;
},
getProductAttrCateList() {
let param = {pageNum: 1, pageSize: 100};
fetchProductAttrCateList(param).then(response => {
this.productAttributeCategoryOptions = [];
let list = response.data.list;
for (let i = 0; i < list.length; i++) {
this.productAttributeCategoryOptions.push({label: list[i].name, value: list[i].id});
}
});
},
getProductAttrList(type, cid) {
let param = {pageNum: 1, pageSize: 100, type: type};
fetchProductAttrList(cid, param).then(response => {
let list = response.data.list;
if (type === 0) {
this.selectProductAttr = [];
for (let i = 0; i < list.length; i++) {
let options = [];
let values = [];
if (this.isEdit) {
if (list[i].handAddStatus === 1) {
//
options = this.getEditAttrOptions(list[i].id);
}
//
values = this.getEditAttrValues(i);
}
this.selectProductAttr.push({
id: list[i].id,
name: list[i].name,
handAddStatus: list[i].handAddStatus,
inputList: list[i].inputList,
values: values,
options: options
});
}
if(this.isEdit){
//
this.refreshProductAttrPics();
}
} else {
this.selectProductParam = [];
for (let i = 0; i < list.length; i++) {
let value=null;
if(this.isEdit){
//
value= this.getEditParamValue(list[i].id);
}
this.selectProductParam.push({
id: list[i].id,
name: list[i].name,
value: value,
inputType: list[i].inputType,
inputList: list[i].inputList
});
}
}
});
},
//
getEditAttrOptions(id) {
let options = [];
for (let i = 0; i < this.value.productAttributeValueList.length; i++) {
let attrValue = this.value.productAttributeValueList[i];
if (attrValue.productAttributeId === id) {
let strArr = attrValue.value.split(',');
for (let j = 0; j < strArr.length; j++) {
options.push(strArr[j]);
}
break;
}
}
return options;
},
//
getEditAttrValues(index) {
let values = new Set();
if (index === 0) {
for (let i = 0; i < this.value.skuStockList.length; i++) {
let sku = this.value.skuStockList[i];
let spData = JSON.parse(sku.spData);
if (spData!= null && spData.length>=1) {
values.add(spData[0].value);
}
}
} else if (index === 1) {
for (let i = 0; i < this.value.skuStockList.length; i++) {
let sku = this.value.skuStockList[i];
let spData = JSON.parse(sku.spData);
if (spData!= null && spData.length>=2) {
values.add(spData[1].value);
}
}
} else {
for (let i = 0; i < this.value.skuStockList.length; i++) {
let sku = this.value.skuStockList[i];
let spData = JSON.parse(sku.spData);
if (spData!= null && spData.length>=3) {
values.add(spData[2].value);
}
}
}
return Array.from(values);
},
//
getEditParamValue(id){
for(let i=0;i<this.value.productAttributeValueList.length;i++){
if(id===this.value.productAttributeValueList[i].productAttributeId){
return this.value.productAttributeValueList[i].value;
}
}
},
handleProductAttrChange(value) {
this.getProductAttrList(0, value);
this.getProductAttrList(1, value);
},
getInputListArr(inputList) {
return inputList.split(',');
},
handleAddProductAttrValue(idx) {
let options = this.selectProductAttr[idx].options;
if (this.addProductAttrValue == null || this.addProductAttrValue == '') {
this.$message({
message: '属性值不能为空',
type: 'warning',
duration: 1000
});
return
}
if (options.indexOf(this.addProductAttrValue) !== -1) {
this.$message({
message: '属性值不能重复',
type: 'warning',
duration: 1000
});
return;
}
this.selectProductAttr[idx].options.push(this.addProductAttrValue);
this.addProductAttrValue = null;
},
handleRemoveProductAttrValue(idx, index) {
this.selectProductAttr[idx].options.splice(index, 1);
},
getProductSkuSp(row, index) {
let spData = JSON.parse(row.spData);
if(spData!=null&&index<spData.length){
return spData[index].value;
}else{
return null;
}
},
handleRefreshProductSkuList() {
this.$confirm('刷新列表将导致sku信息重新生成是否要刷新', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.refreshProductAttrPics();
this.refreshProductSkuList();
});
},
handleSyncProductSkuPrice(){
this.$confirm('将同步第一个sku的价格到所有sku,是否继续', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if(this.value.skuStockList!==null&&this.value.skuStockList.length>0){
let tempSkuList = [];
tempSkuList = tempSkuList.concat(tempSkuList,this.value.skuStockList);
let price=this.value.skuStockList[0].price;
for(let i=0;i<tempSkuList.length;i++){
tempSkuList[i].price=price;
}
this.value.skuStockList=[];
this.value.skuStockList=this.value.skuStockList.concat(this.value.skuStockList,tempSkuList);
}
});
},
handleSyncProductSkuStock(){
this.$confirm('将同步第一个sku的库存到所有sku,是否继续', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if(this.value.skuStockList!==null&&this.value.skuStockList.length>0){
let tempSkuList = [];
tempSkuList = tempSkuList.concat(tempSkuList,this.value.skuStockList);
let stock=this.value.skuStockList[0].stock;
let lowStock=this.value.skuStockList[0].lowStock;
for(let i=0;i<tempSkuList.length;i++){
tempSkuList[i].stock=stock;
tempSkuList[i].lowStock=lowStock;
}
this.value.skuStockList=[];
this.value.skuStockList=this.value.skuStockList.concat(this.value.skuStockList,tempSkuList);
}
});
},
refreshProductSkuList() {
this.value.skuStockList = [];
let skuList = this.value.skuStockList;
//
if (this.selectProductAttr.length === 1) {
let attr = this.selectProductAttr[0];
for (let i = 0; i < attr.values.length; i++) {
skuList.push({
spData: JSON.stringify([{key:attr.name,value:attr.values[i]}])
});
}
} else if (this.selectProductAttr.length === 2) {
let attr0 = this.selectProductAttr[0];
let attr1 = this.selectProductAttr[1];
for (let i = 0; i < attr0.values.length; i++) {
if (attr1.values.length === 0) {
skuList.push({
spData: JSON.stringify([{key:attr0.name,value:attr0.values[i]}])
});
continue;
}
for (let j = 0; j < attr1.values.length; j++) {
let spData = [];
spData.push({key:attr0.name,value:attr0.values[i]});
spData.push({key:attr1.name,value:attr1.values[j]});
skuList.push({
spData: JSON.stringify(spData)
});
}
}
} else {
let attr0 = this.selectProductAttr[0];
let attr1 = this.selectProductAttr[1];
let attr2 = this.selectProductAttr[2];
for (let i = 0; i < attr0.values.length; i++) {
if (attr1.values.length === 0) {
skuList.push({
spData: JSON.stringify([{key:attr0.name,value:attr0.values[i]}])
});
continue;
}
for (let j = 0; j < attr1.values.length; j++) {
if (attr2.values.length === 0) {
let spData = [];
spData.push({key:attr0.name,value:attr0.values[i]});
spData.push({key:attr1.name,value:attr1.values[j]});
skuList.push({
spData: JSON.stringify(spData)
});
continue;
}
for (let k = 0; k < attr2.values.length; k++) {
let spData = [];
spData.push({key:attr0.name,value:attr0.values[i]});
spData.push({key:attr1.name,value:attr1.values[j]});
spData.push({key:attr2.name,value:attr2.values[k]});
skuList.push({
spData: JSON.stringify(spData)
});
}
}
}
}
},
refreshProductAttrPics() {
this.selectProductAttrPics = [];
if (this.selectProductAttr.length >= 1) {
let values = this.selectProductAttr[0].values;
for (let i = 0; i < values.length; i++) {
let pic=null;
if(this.isEdit){
//
pic=this.getProductSkuPic(values[i]);
}
this.selectProductAttrPics.push({name: values[i], pic: pic})
}
}
},
//
getProductSkuPic(name){
for(let i=0;i<this.value.skuStockList.length;i++){
let spData = JSON.parse(this.value.skuStockList[i].spData);
if(name===spData[0].value){
return this.value.skuStockList[i].pic;
}
}
return null;
},
//
mergeProductAttrValue() {
this.value.productAttributeValueList = [];
for (let i = 0; i < this.selectProductAttr.length; i++) {
let attr = this.selectProductAttr[i];
if (attr.handAddStatus === 1 && attr.options != null && attr.options.length > 0) {
this.value.productAttributeValueList.push({
productAttributeId: attr.id,
value: this.getOptionStr(attr.options)
});
}
}
for (let i = 0; i < this.selectProductParam.length; i++) {
let param = this.selectProductParam[i];
this.value.productAttributeValueList.push({
productAttributeId: param.id,
value: param.value
});
}
},
//
mergeProductAttrPics() {
for (let i = 0; i < this.selectProductAttrPics.length; i++) {
for (let j = 0; j < this.value.skuStockList.length; j++) {
let spData = JSON.parse(this.value.skuStockList[j].spData);
if (spData[0].value === this.selectProductAttrPics[i].name) {
this.value.skuStockList[j].pic = this.selectProductAttrPics[i].pic;
}
}
}
},
getOptionStr(arr) {
let str = '';
for (let i = 0; i < arr.length; i++) {
str += arr[i];
if (i != arr.length - 1) {
str += ',';
}
}
return str;
},
handleRemoveProductSku(index, row) {
let list = this.value.skuStockList;
if (list.length === 1) {
list.pop();
} else {
list.splice(index, 1);
}
},
getParamInputList(inputList) {
return inputList.split(',');
},
handlePrev() {
this.$emit('prevStep')
},
handleNext() {
this.mergeProductAttrValue();
this.mergeProductAttrPics();
this.$emit('nextStep')
}
} }
} }
};
</script> </script>
<style scoped> <style scoped>
.littleMarginLeft { .littleMarginLeft {
margin-left: 10px; margin-left: 10px;
} }
.littleMarginTop {
margin-top: 10px;
}
.paramInput {
width: 250px;
}
.paramInputLabel { .cardBg {
display: inline-block; background: #f8f9fc;
width: 100px; }
text-align: right;
padding-right: 10px
}
.cardBg {
background: #F8F9FC;
}
</style> </style>

@ -1,17 +1,23 @@
<template>  <template>
<!-- 商品详情表单容器 -->
<el-card class="form-container" shadow="never"> <el-card class="form-container" shadow="never">
<!-- 步骤条显示当前所处的步骤 -->
<el-steps :active="active" finish-status="success" align-center> <el-steps :active="active" finish-status="success" align-center>
<el-step title="填写商品信息"></el-step> <el-step title="填写商品信息"></el-step>
<el-step title="填写商品促销"></el-step> <el-step title="填写商品促销"></el-step>
<el-step title="填写商品属性"></el-step> <el-step title="填写商品属性"></el-step>
<el-step title="选择商品关联"></el-step> <el-step title="选择商品关联"></el-step>
</el-steps> </el-steps>
<!-- 商品基本信息组件 -->
<product-info-detail <product-info-detail
v-show="showStatus[0]" v-show="showStatus[0]"
v-model="productParam" v-model="productParam"
:is-edit="isEdit" :is-edit="isEdit"
@nextStep="nextStep"> @nextStep="nextStep">
</product-info-detail> </product-info-detail>
<!-- 商品促销信息组件 -->
<product-sale-detail <product-sale-detail
v-show="showStatus[1]" v-show="showStatus[1]"
v-model="productParam" v-model="productParam"
@ -19,6 +25,8 @@
@nextStep="nextStep" @nextStep="nextStep"
@prevStep="prevStep"> @prevStep="prevStep">
</product-sale-detail> </product-sale-detail>
<!-- 商品属性组件 -->
<product-attr-detail <product-attr-detail
v-show="showStatus[2]" v-show="showStatus[2]"
v-model="productParam" v-model="productParam"
@ -26,6 +34,8 @@
@nextStep="nextStep" @nextStep="nextStep"
@prevStep="prevStep"> @prevStep="prevStep">
</product-attr-detail> </product-attr-detail>
<!-- 商品关联信息组件 -->
<product-relation-detail <product-relation-detail
v-show="showStatus[3]" v-show="showStatus[3]"
v-model="productParam" v-model="productParam"
@ -35,154 +45,140 @@
</product-relation-detail> </product-relation-detail>
</el-card> </el-card>
</template> </template>
<script> <script>
import ProductInfoDetail from './ProductInfoDetail'; import ProductInfoDetail from './ProductInfoDetail'; //
import ProductSaleDetail from './ProductSaleDetail'; import ProductSaleDetail from './ProductSaleDetail'; //
import ProductAttrDetail from './ProductAttrDetail'; import ProductAttrDetail from './ProductAttrDetail'; //
import ProductRelationDetail from './ProductRelationDetail'; import ProductRelationDetail from './ProductRelationDetail'; //
import {createProduct,getProduct,updateProduct} from '@/api/product'; import { createProduct, getProduct, updateProduct } from '@/api/product'; // API
const defaultProductParam = { //
albumPics: '', const defaultProductParam = {
brandId: null, albumPics: '', //
brandName: '', brandId: null, // ID
deleteStatus: 0, brandName: '', //
description: '', deleteStatus: 0, //
detailDesc: '', description: '', //
detailHtml: '', detailDesc: '', //
detailMobileHtml: '', detailHtml: '', //
detailTitle: '', detailMobileHtml: '', //
feightTemplateId: 0, detailTitle: '', //
flashPromotionCount: 0, feightTemplateId: 0, // ID
flashPromotionId: 0, flashPromotionCount: 0, //
flashPromotionPrice: 0, flashPromotionPrice: 0, //
flashPromotionSort: 0, giftPoint: 0, //
giftPoint: 0, giftGrowth: 0, //
giftGrowth: 0, keywords: '', //
keywords: '', lowStock: 0, //
lowStock: 0, name: '', //
name: '', newStatus: 0, //
newStatus: 0, originalPrice: 0, //
note: '', price: 0, //
originalPrice: 0, skuStockList: [], // SKU
pic: '', productAttributeValueList: [], //
//{memberLevelId: 0,memberPrice: 0,memberLevelName: null} promotionStartTime: '', //
memberPriceList: [], promotionEndTime: '', //
// publishStatus: 0, //
productFullReductionList: [{fullPrice: 0, reducePrice: 0}], sale: 0, //
// stock: 0, //
productLadderList: [{count: 0,discount: 0,price: 0}], };
previewStatus: 0,
price: 0, export default {
productAttributeCategoryId: null, name: 'ProductDetail', //
//{productAttributeId: 0, value: ''} components: {
productAttributeValueList: [], ProductInfoDetail,
//sku{lowStock: 0, pic: '', price: 0, sale: 0, skuCode: '', spData: '', stock: 0} ProductSaleDetail,
skuStockList: [], ProductAttrDetail,
//{subjectId: 0} ProductRelationDetail,
subjectProductRelationList: [], },
//{prefrenceAreaId: 0} props: {
prefrenceAreaProductRelationList: [], isEdit: {
productCategoryId: null, type: Boolean,
productCategoryName: '', default: false, //
productSn: '', },
promotionEndTime: '', },
promotionPerLimit: 0, data() {
promotionPrice: null, return {
promotionStartTime: '', active: 0, //
promotionType: 0, productParam: Object.assign({}, defaultProductParam), //
publishStatus: 0, showStatus: [true, false, false, false], //
recommandStatus: 0, };
sale: 0, },
serviceIds: '', created() {
sort: 0, //
stock: 0, if (this.isEdit) {
subTitle: '', getProduct(this.$route.query.id).then((response) => {
unit: '', this.productParam = response.data; //
usePointLimit: 0, });
verifyStatus: 0, }
weight: 0 },
}; methods: {
export default { //
name: 'ProductDetail', hideAll() {
components: {ProductInfoDetail, ProductSaleDetail, ProductAttrDetail, ProductRelationDetail}, for (let i = 0; i < this.showStatus.length; i++) {
props: { this.showStatus[i] = false;
isEdit: {
type: Boolean,
default: false
} }
}, },
data() { //
return { prevStep() {
active: 0, if (this.active > 0) {
productParam: Object.assign({}, defaultProductParam), this.active--; // 退
showStatus: [true, false, false, false] this.hideAll(); //
this.showStatus[this.active] = true; //
} }
}, },
created(){ //
if(this.isEdit){ nextStep() {
getProduct(this.$route.query.id).then(response=>{ if (this.active < this.showStatus.length - 1) {
this.productParam=response.data; this.active++; //
}); this.hideAll(); //
this.showStatus[this.active] = true; //
} }
}, },
methods: { //
hideAll() { finishCommit(isEdit) {
for (let i = 0; i < this.showStatus.length; i++) { this.$confirm('是否要提交该产品?', '提示', {
this.showStatus[i] = false; confirmButtonText: '确定',
} cancelButtonText: '取消',
}, type: 'warning',
prevStep() { }).then(() => {
if (this.active > 0 && this.active < this.showStatus.length) { if (isEdit) {
this.active--; //
this.hideAll(); updateProduct(this.$route.query.id, this.productParam).then(() => {
this.showStatus[this.active] = true; this.$message({
} type: 'success',
}, message: '修改成功',
nextStep() { duration: 1000,
if (this.active < this.showStatus.length - 1) {
this.active++;
this.hideAll();
this.showStatus[this.active] = true;
}
},
finishCommit(isEdit) {
this.$confirm('是否要提交该产品', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if(isEdit){
updateProduct(this.$route.query.id,this.productParam).then(response=>{
this.$message({
type: 'success',
message: '提交成功',
duration:1000
});
this.$router.back();
}); });
}else{ this.$router.back(); //
createProduct(this.productParam).then(response=>{ });
this.$message({ } else {
type: 'success', //
message: '提交成功', createProduct(this.productParam).then(() => {
duration:1000 this.$message({
}); type: 'success',
location.reload(); message: '提交成功',
duration: 1000,
}); });
} location.reload(); //
}) });
} }
} });
} },
},
};
</script> </script>
<style>
.form-container {
width: 960px;
}
.form-inner-container {
width: 800px;
}
</style>
<style>
/* 容器样式 */
.form-container {
width: 960px;
margin: 0 auto;
}
.form-inner-container {
width: 800px;
margin: 0 auto;
}
</style>

@ -1,200 +1,247 @@
<template> <template>
<!-- 商品基本信息表单 -->
<div style="margin-top: 50px"> <div style="margin-top: 50px">
<el-form :model="value" :rules="rules" ref="productInfoForm" label-width="120px" class="form-inner-container" size="small"> <el-form
:model="value"
:rules="rules"
ref="productInfoForm"
label-width="120px"
class="form-inner-container"
size="small"
>
<!-- 商品分类选择 -->
<el-form-item label="商品分类:" prop="productCategoryId"> <el-form-item label="商品分类:" prop="productCategoryId">
<el-cascader <el-cascader
v-model="selectProductCateValue" v-model="selectProductCateValue"
:options="productCateOptions"> :options="productCateOptions"
</el-cascader> ></el-cascader>
</el-form-item> </el-form-item>
<!-- 商品名称输入框 -->
<el-form-item label="商品名称:" prop="name"> <el-form-item label="商品名称:" prop="name">
<el-input v-model="value.name"></el-input> <el-input v-model="value.name"></el-input>
</el-form-item> </el-form-item>
<!-- 商品副标题输入框 -->
<el-form-item label="副标题:" prop="subTitle"> <el-form-item label="副标题:" prop="subTitle">
<el-input v-model="value.subTitle"></el-input> <el-input v-model="value.subTitle"></el-input>
</el-form-item> </el-form-item>
<!-- 商品品牌选择框 -->
<el-form-item label="商品品牌:" prop="brandId"> <el-form-item label="商品品牌:" prop="brandId">
<el-select <el-select
v-model="value.brandId" v-model="value.brandId"
@change="handleBrandChange" @change="handleBrandChange"
placeholder="请选择品牌"> placeholder="请选择品牌"
>
<el-option <el-option
v-for="item in brandOptions" v-for="item in brandOptions"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"> :value="item.value"
</el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 商品介绍文本域 -->
<el-form-item label="商品介绍:"> <el-form-item label="商品介绍:">
<el-input <el-input
:autoSize="true" :autoSize="true"
v-model="value.description" v-model="value.description"
type="textarea" type="textarea"
placeholder="请输入内容"></el-input> placeholder="请输入内容"
></el-input>
</el-form-item> </el-form-item>
<!-- 商品货号 -->
<el-form-item label="商品货号:"> <el-form-item label="商品货号:">
<el-input v-model="value.productSn"></el-input> <el-input v-model="value.productSn"></el-input>
</el-form-item> </el-form-item>
<!-- 商品售价 -->
<el-form-item label="商品售价:"> <el-form-item label="商品售价:">
<el-input v-model="value.price"></el-input> <el-input v-model="value.price"></el-input>
</el-form-item> </el-form-item>
<!-- 市场价 -->
<el-form-item label="市场价:"> <el-form-item label="市场价:">
<el-input v-model="value.originalPrice"></el-input> <el-input v-model="value.originalPrice"></el-input>
</el-form-item> </el-form-item>
<!-- 商品库存 -->
<el-form-item label="商品库存:"> <el-form-item label="商品库存:">
<el-input v-model="value.stock"></el-input> <el-input v-model="value.stock"></el-input>
</el-form-item> </el-form-item>
<!-- 计量单位 -->
<el-form-item label="计量单位:"> <el-form-item label="计量单位:">
<el-input v-model="value.unit"></el-input> <el-input v-model="value.unit"></el-input>
</el-form-item> </el-form-item>
<!-- 商品重量 -->
<el-form-item label="商品重量:"> <el-form-item label="商品重量:">
<el-input v-model="value.weight" style="width: 300px"></el-input> <el-input v-model="value.weight" style="width: 300px"></el-input>
<span style="margin-left: 20px"></span> <span style="margin-left: 20px"></span>
</el-form-item> </el-form-item>
<!-- 排序 -->
<el-form-item label="排序"> <el-form-item label="排序">
<el-input v-model="value.sort"></el-input> <el-input v-model="value.sort"></el-input>
</el-form-item> </el-form-item>
<!-- 下一步按钮 -->
<el-form-item style="text-align: center"> <el-form-item style="text-align: center">
<el-button type="primary" size="medium" @click="handleNext('productInfoForm')"></el-button> <el-button
type="primary"
size="medium"
@click="handleNext('productInfoForm')"
>
下一步填写商品促销
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</template> </template>
<script> <script>
import {fetchListWithChildren} from '@/api/productCate' import { fetchListWithChildren } from '@/api/productCate'; //
import {fetchList as fetchBrandList} from '@/api/brand' import { fetchList as fetchBrandList } from '@/api/brand'; //
import {getProduct} from '@/api/product';
export default {
export default { name: 'ProductInfoDetail', //
name: "ProductInfoDetail", props: {
props: { value: Object, //
value: Object, isEdit: {
isEdit: { type: Boolean, //
type: Boolean, default: false,
default: false
}
}, },
data() { },
return { data() {
hasEditCreated:false, return {
// hasEditCreated: false, //
selectProductCateValue: [], selectProductCateValue: [], //
productCateOptions: [], productCateOptions: [], //
brandOptions: [], brandOptions: [], //
rules: { rules: {
name: [ name: [
{required: true, message: '请输入商品名称', trigger: 'blur'}, { required: true, message: '请输入商品名称', trigger: 'blur' },
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'} { min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur' },
], ],
subTitle: [{required: true, message: '请输入商品副标题', trigger: 'blur'}], subTitle: [{ required: true, message: '请输入商品副标题', trigger: 'blur' }],
productCategoryId: [{required: true, message: '请选择商品分类', trigger: 'blur'}], productCategoryId: [
brandId: [{required: true, message: '请选择商品品牌', trigger: 'blur'}], { required: true, message: '请选择商品分类', trigger: 'blur' },
description: [{required: true, message: '请输入商品介绍', trigger: 'blur'}], ],
requiredProp: [{required: true, message: '该项为必填项', trigger: 'blur'}] brandId: [{ required: true, message: '请选择商品品牌', trigger: 'blur' }],
} description: [{ required: true, message: '请输入商品介绍', trigger: 'blur' }],
}; },
};
},
created() {
//
this.getProductCateList();
this.getBrandList();
},
computed: {
// ID
productId() {
return this.value.id;
}, },
created() { },
this.getProductCateList(); watch: {
this.getBrandList(); productId(newValue) {
if (!this.isEdit || this.hasEditCreated || !newValue) return;
this.handleEditCreated();
}, },
computed:{ selectProductCateValue(newValue) {
// // ID
productId(){ if (newValue && newValue.length === 2) {
return this.value.id; this.value.productCategoryId = newValue[1];
this.value.productCategoryName = this.getCateNameById(this.value.productCategoryId);
} else {
this.value.productCategoryId = null;
this.value.productCategoryName = null;
} }
}, },
watch: { },
productId:function(newValue){ methods: {
if(!this.isEdit)return; //
if(this.hasEditCreated)return; handleEditCreated() {
if(newValue===undefined||newValue==null||newValue===0)return; if (this.value.productCategoryId != null) {
this.handleEditCreated(); this.selectProductCateValue = [
}, this.value.cateParentId,
selectProductCateValue: function (newValue) { this.value.productCategoryId,
if (newValue != null && newValue.length === 2) { ];
this.value.productCategoryId = newValue[1];
this.value.productCategoryName= this.getCateNameById(this.value.productCategoryId);
} else {
this.value.productCategoryId = null;
this.value.productCategoryName=null;
}
} }
this.hasEditCreated = true;
}, },
methods: {
// //
handleEditCreated(){ getProductCateList() {
if(this.value.productCategoryId!=null){ fetchListWithChildren().then((response) => {
this.selectProductCateValue.push(this.value.cateParentId); let list = response.data;
this.selectProductCateValue.push(this.value.productCategoryId); this.productCateOptions = list.map((item) => ({
} label: item.name,
this.hasEditCreated=true; value: item.id,
}, children: item.children?.map((child) => ({
getProductCateList() { label: child.name,
fetchListWithChildren().then(response => { value: child.id,
let list = response.data; })),
this.productCateOptions = []; }));
for (let i = 0; i < list.length; i++) { });
let children = []; },
if (list[i].children != null && list[i].children.length > 0) {
for (let j = 0; j < list[i].children.length; j++) { //
children.push({label: list[i].children[j].name, value: list[i].children[j].id}); getBrandList() {
} fetchBrandList({ pageNum: 1, pageSize: 100 }).then((response) => {
} this.brandOptions = response.data.list.map((item) => ({
this.productCateOptions.push({label: list[i].name, value: list[i].id, children: children}); label: item.name,
} value: item.id,
}); }));
}, });
getBrandList() { },
fetchBrandList({pageNum: 1, pageSize: 100}).then(response => {
this.brandOptions = []; // ID
let brandList = response.data.list; getCateNameById(id) {
for (let i = 0; i < brandList.length; i++) { for (const parent of this.productCateOptions) {
this.brandOptions.push({label: brandList[i].name, value: brandList[i].id}); for (const child of parent.children || []) {
} if (child.value === id) {
}); return child.label;
},
getCateNameById(id){
let name=null;
for(let i=0;i<this.productCateOptions.length;i++){
for(let j=0;j<this.productCateOptions[i].children.length;j++){
if(this.productCateOptions[i].children[j].value===id){
name=this.productCateOptions[i].children[j].label;
return name;
}
}
}
return name;
},
handleNext(formName){
this.$refs[formName].validate((valid) => {
if (valid) {
this.$emit('nextStep');
} else {
this.$message({
message: '验证失败',
type: 'error',
duration:1000
});
return false;
}
});
},
handleBrandChange(val) {
let brandName = '';
for (let i = 0; i < this.brandOptions.length; i++) {
if (this.brandOptions[i].value === val) {
brandName = this.brandOptions[i].label;
break;
} }
} }
this.value.brandName = brandName;
} }
} return null;
} },
//
handleNext(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$emit('nextStep'); //
} else {
this.$message({
message: '验证失败',
type: 'error',
duration: 1000,
});
}
});
},
//
handleBrandChange(val) {
const selectedBrand = this.brandOptions.find(
(item) => item.value === val
);
this.value.brandName = selectedBrand ? selectedBrand.label : '';
},
},
};
</script> </script>
<style scoped> <style scoped>
/* 表单样式居中 */
.form-inner-container {
max-width: 600px;
margin: 0 auto;
}
</style> </style>

@ -1,10 +1,14 @@
<template> <template>
<!-- 商品关联表单 -->
<div style="margin-top: 50px"> <div style="margin-top: 50px">
<el-form :model="value" <el-form
ref="productRelationForm" :model="value"
label-width="120px" ref="productRelationForm"
class="form-inner-container" label-width="120px"
size="small"> class="form-inner-container"
size="small"
>
<!-- 关联专题 Transfer 组件 -->
<el-form-item label="关联专题:"> <el-form-item label="关联专题:">
<el-transfer <el-transfer
style="display: inline-block" style="display: inline-block"
@ -13,9 +17,11 @@
filter-placeholder="请输入专题名称" filter-placeholder="请输入专题名称"
v-model="selectSubject" v-model="selectSubject"
:titles="subjectTitles" :titles="subjectTitles"
:data="subjectList"> :data="subjectList"
</el-transfer> ></el-transfer>
</el-form-item> </el-form-item>
<!-- 关联优选 Transfer 组件 -->
<el-form-item label="关联优选:"> <el-form-item label="关联优选:">
<el-transfer <el-transfer
style="display: inline-block" style="display: inline-block"
@ -24,122 +30,166 @@
filter-placeholder="请输入优选名称" filter-placeholder="请输入优选名称"
v-model="selectPrefrenceArea" v-model="selectPrefrenceArea"
:titles="prefrenceAreaTitles" :titles="prefrenceAreaTitles"
:data="prefrenceAreaList"> :data="prefrenceAreaList"
</el-transfer> ></el-transfer>
</el-form-item> </el-form-item>
<!-- 操作按钮 -->
<el-form-item style="text-align: center"> <el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button> <el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleFinishCommit"></el-button> <el-button type="primary" size="medium" @click="handleFinishCommit">
完成提交商品
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</template> </template>
<script> <script>
import {fetchListAll as fetchSubjectList} from '@/api/subject' import { fetchListAll as fetchSubjectList } from '@/api/subject'; // API
import {fetchList as fetchPrefrenceAreaList} from '@/api/prefrenceArea' import { fetchList as fetchPrefrenceAreaList } from '@/api/prefrenceArea'; // API
export default { export default {
name: "ProductRelationDetail", name: 'ProductRelationDetail', //
props: { props: {
value: Object, value: Object, //
isEdit: { isEdit: {
type: Boolean, type: Boolean, //
default: false default: false,
}
},
data() {
return {
//
subjectList: [],
//
subjectTitles: ['待选择', '已选择'],
//
prefrenceAreaList: [],
//
prefrenceAreaTitles: ['待选择', '已选择']
};
},
created() {
this.getSubjectList();
this.getPrefrenceAreaList();
}, },
computed:{ },
// data() {
selectSubject:{ return {
get:function () { subjectList: [], //
let subjects =[]; subjectTitles: ['待选择', '已选择'], // Transfer
if(this.value.subjectProductRelationList==null||this.value.subjectProductRelationList.length<=0){ prefrenceAreaList: [], //
return subjects; prefrenceAreaTitles: ['待选择', '已选择'], // Transfer
} };
for(let i=0;i<this.value.subjectProductRelationList.length;i++){ },
subjects.push(this.value.subjectProductRelationList[i].subjectId); created() {
} this.getSubjectList(); //
this.getPrefrenceAreaList(); //
},
computed: {
/**
* 选中的专题通过 v-model computed 绑定选中的专题数据
*/
selectSubject: {
get() {
let subjects = [];
if (
this.value.subjectProductRelationList == null ||
this.value.subjectProductRelationList.length <= 0
) {
return subjects; return subjects;
},
set:function (newValue) {
this.value.subjectProductRelationList=[];
for(let i=0;i<newValue.length;i++){
this.value.subjectProductRelationList.push({subjectId:newValue[i]});
}
} }
}, // ID
// for (let i = 0; i < this.value.subjectProductRelationList.length; i++) {
selectPrefrenceArea:{ subjects.push(this.value.subjectProductRelationList[i].subjectId);
get:function () {
let prefrenceAreas =[];
if(this.value.prefrenceAreaProductRelationList==null||this.value.prefrenceAreaProductRelationList.length<=0){
return prefrenceAreas;
}
for(let i=0;i<this.value.prefrenceAreaProductRelationList.length;i++){
prefrenceAreas.push(this.value.prefrenceAreaProductRelationList[i].prefrenceAreaId);
}
return prefrenceAreas;
},
set:function (newValue) {
this.value.prefrenceAreaProductRelationList=[];
for(let i=0;i<newValue.length;i++){
this.value.prefrenceAreaProductRelationList.push({prefrenceAreaId:newValue[i]});
}
} }
} return subjects;
},
methods: {
filterMethod(query, item) {
return item.label.indexOf(query) > -1;
}, },
getSubjectList() { set(newValue) {
fetchSubjectList().then(response => { //
let list = response.data; this.value.subjectProductRelationList = [];
for (let i = 0; i < list.length; i++) { for (let i = 0; i < newValue.length; i++) {
this.subjectList.push({ this.value.subjectProductRelationList.push({ subjectId: newValue[i] });
label: list[i].title, }
key: list[i].id
});
}
});
}, },
getPrefrenceAreaList() { },
fetchPrefrenceAreaList().then(response=>{
let list = response.data; /**
for (let i = 0; i < list.length; i++) { * 选中的优选通过 v-model computed 绑定选中的优选数据
this.prefrenceAreaList.push({ */
label: list[i].name, selectPrefrenceArea: {
key: list[i].id get() {
}); let prefrenceAreas = [];
} if (
}); this.value.prefrenceAreaProductRelationList == null ||
this.value.prefrenceAreaProductRelationList.length <= 0
) {
return prefrenceAreas;
}
// ID
for (
let i = 0;
i < this.value.prefrenceAreaProductRelationList.length;
i++
) {
prefrenceAreas.push(
this.value.prefrenceAreaProductRelationList[i].prefrenceAreaId
);
}
return prefrenceAreas;
}, },
handlePrev(){ set(newValue) {
this.$emit('prevStep') //
this.value.prefrenceAreaProductRelationList = [];
for (let i = 0; i < newValue.length; i++) {
this.value.prefrenceAreaProductRelationList.push({
prefrenceAreaId: newValue[i],
});
}
}, },
handleFinishCommit(){ },
this.$emit('finishCommit',this.isEdit); },
} methods: {
} /**
} * 筛选方法根据关键词过滤 Transfer 组件数据
* @param {String} query - 查询关键词
* @param {Object} item - 当前项
*/
filterMethod(query, item) {
return item.label.indexOf(query) > -1;
},
/**
* 获取专题列表数据
*/
getSubjectList() {
fetchSubjectList().then((response) => {
let list = response.data;
this.subjectList = list.map((item) => ({
label: item.title,
key: item.id,
}));
});
},
/**
* 获取优选列表数据
*/
getPrefrenceAreaList() {
fetchPrefrenceAreaList().then((response) => {
let list = response.data;
this.prefrenceAreaList = list.map((item) => ({
label: item.name,
key: item.id,
}));
});
},
/**
* 上一步操作通知父组件返回上一步
*/
handlePrev() {
this.$emit('prevStep');
},
/**
* 完成提交通知父组件提交表单数据
*/
handleFinishCommit() {
this.$emit('finishCommit', this.isEdit);
},
},
};
</script> </script>
<style scoped> <style scoped>
/* 局部样式:可根据需要自定义样式 */
.form-inner-container {
max-width: 600px;
margin: 0 auto;
}
</style> </style>

@ -1,43 +1,63 @@
<template> <template>
<!-- 商品促销信息表单 -->
<div style="margin-top: 50px"> <div style="margin-top: 50px">
<el-form :model="value" ref="productSaleForm" label-width="120px" class="form-inner-container" size="small"> <el-form
:model="value"
ref="productSaleForm"
label-width="120px"
class="form-inner-container"
size="small"
>
<!-- 赠送积分 -->
<el-form-item label="赠送积分:"> <el-form-item label="赠送积分:">
<el-input v-model="value.giftPoint"></el-input> <el-input v-model="value.giftPoint"></el-input>
</el-form-item> </el-form-item>
<!-- 赠送成长值 -->
<el-form-item label="赠送成长值:"> <el-form-item label="赠送成长值:">
<el-input v-model="value.giftGrowth"></el-input> <el-input v-model="value.giftGrowth"></el-input>
</el-form-item> </el-form-item>
<!-- 积分购买限制 -->
<el-form-item label="积分购买限制:"> <el-form-item label="积分购买限制:">
<el-input v-model="value.usePointLimit"></el-input> <el-input v-model="value.usePointLimit"></el-input>
</el-form-item> </el-form-item>
<!-- 预告商品开关 -->
<el-form-item label="预告商品:"> <el-form-item label="预告商品:">
<el-switch <el-switch
v-model="value.previewStatus" v-model="value.previewStatus"
:active-value="1" :active-value="1"
:inactive-value="0"> :inactive-value="0"
</el-switch> ></el-switch>
</el-form-item> </el-form-item>
<!-- 商品上架开关 -->
<el-form-item label="商品上架:"> <el-form-item label="商品上架:">
<el-switch <el-switch
v-model="value.publishStatus" v-model="value.publishStatus"
:active-value="1" :active-value="1"
:inactive-value="0"> :inactive-value="0"
</el-switch> ></el-switch>
</el-form-item> </el-form-item>
<!-- 商品推荐新品与推荐开关 -->
<el-form-item label="商品推荐:"> <el-form-item label="商品推荐:">
<span style="margin-right: 10px">新品</span> <span style="margin-right: 10px">新品</span>
<el-switch <el-switch
v-model="value.newStatus" v-model="value.newStatus"
:active-value="1" :active-value="1"
:inactive-value="0"> :inactive-value="0"
</el-switch> ></el-switch>
<span style="margin-left: 10px;margin-right: 10px">推荐</span> <span style="margin-left: 10px; margin-right: 10px">推荐</span>
<el-switch <el-switch
v-model="value.recommandStatus" v-model="value.recommandStatus"
:active-value="1" :active-value="1"
:inactive-value="0"> :inactive-value="0"
</el-switch> ></el-switch>
</el-form-item> </el-form-item>
<!-- 服务保证复选框组 -->
<el-form-item label="服务保证:"> <el-form-item label="服务保证:">
<el-checkbox-group v-model="selectServiceList"> <el-checkbox-group v-model="selectServiceList">
<el-checkbox :label="1">无忧退货</el-checkbox> <el-checkbox :label="1">无忧退货</el-checkbox>
@ -45,18 +65,32 @@
<el-checkbox :label="3">免费包邮</el-checkbox> <el-checkbox :label="3">免费包邮</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
<!-- 详细页标题 -->
<el-form-item label="详细页标题:"> <el-form-item label="详细页标题:">
<el-input v-model="value.detailTitle"></el-input> <el-input v-model="value.detailTitle"></el-input>
</el-form-item> </el-form-item>
<!-- 详细页描述 -->
<el-form-item label="详细页描述:"> <el-form-item label="详细页描述:">
<el-input v-model="value.detailDesc"></el-input> <el-input v-model="value.detailDesc"></el-input>
</el-form-item> </el-form-item>
<!-- 商品关键字 -->
<el-form-item label="商品关键字:"> <el-form-item label="商品关键字:">
<el-input v-model="value.keywords"></el-input> <el-input v-model="value.keywords"></el-input>
</el-form-item> </el-form-item>
<!-- 商品备注 -->
<el-form-item label="商品备注:"> <el-form-item label="商品备注:">
<el-input v-model="value.note" type="textarea" :autoSize="true"></el-input> <el-input
v-model="value.note"
type="textarea"
:autoSize="true"
></el-input>
</el-form-item> </el-form-item>
<!-- 选择优惠方式 -->
<el-form-item label="选择优惠方式:"> <el-form-item label="选择优惠方式:">
<el-radio-group v-model="value.promotionType" size="small"> <el-radio-group v-model="value.promotionType" size="small">
<el-radio-button :label="0">无优惠</el-radio-button> <el-radio-button :label="0">无优惠</el-radio-button>
@ -66,15 +100,17 @@
<el-radio-button :label="4">满减价格</el-radio-button> <el-radio-button :label="4">满减价格</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-show="value.promotionType===1">
<!-- 特惠促销设置 -->
<el-form-item v-show="value.promotionType === 1">
<div> <div>
开始时间 开始时间
<el-date-picker <el-date-picker
v-model="value.promotionStartTime" v-model="value.promotionStartTime"
type="datetime" type="datetime"
:picker-options="pickerOptions1" :picker-options="pickerOptions1"
placeholder="选择开始时间"> placeholder="选择开始时间"
</el-date-picker> ></el-date-picker>
</div> </div>
<div class="littleMargin"> <div class="littleMargin">
结束时间 结束时间
@ -82,225 +118,156 @@
v-model="value.promotionEndTime" v-model="value.promotionEndTime"
type="datetime" type="datetime"
:picker-options="pickerOptions1" :picker-options="pickerOptions1"
placeholder="选择结束时间"> placeholder="选择结束时间"
</el-date-picker> ></el-date-picker>
</div> </div>
<div class="littleMargin"> <div class="littleMargin">
促销价格 促销价格
<el-input style="width: 220px" v-model="value.promotionPrice" placeholder="输入促销价格"></el-input> <el-input
style="width: 220px"
v-model="value.promotionPrice"
placeholder="输入促销价格"
></el-input>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item v-show="value.promotionType===2">
<div v-for="(item, index) in value.memberPriceList" :class="{littleMargin:index!==0}"> <!-- 会员价格设置 -->
{{item.memberLevelName}} <el-form-item v-show="value.promotionType === 2">
<div v-for="(item, index) in value.memberPriceList" :key="index" class="littleMargin">
{{ item.memberLevelName }}
<el-input v-model="item.memberPrice" style="width: 200px"></el-input> <el-input v-model="item.memberPrice" style="width: 200px"></el-input>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item v-show="value.promotionType===3">
<el-table :data="value.productLadderList" <!-- 阶梯价格设置 -->
style="width: 80%" border> <el-form-item v-show="value.promotionType === 3">
<el-table-column <el-table :data="value.productLadderList" style="width: 80%" border>
label="数量" <el-table-column label="数量" align="center" width="120">
align="center"
width="120">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.count"></el-input> <el-input v-model="scope.row.count"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="折扣" align="center" width="120">
label="折扣"
align="center"
width="120">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.discount"></el-input> <el-input v-model="scope.row.discount"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="操作" align="center">
align="center"
label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" @click="handleRemoveProductLadder(scope.$index, scope.row)">删除</el-button> <el-button type="text" @click="handleRemoveProductLadder(scope.$index)">
<el-button type="text" @click="handleAddProductLadder(scope.$index, scope.row)">添加</el-button> 删除
</el-button>
<el-button type="text" @click="handleAddProductLadder(scope.$index)">
添加
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-form-item> </el-form-item>
<el-form-item v-show="value.promotionType===4">
<el-table :data="value.productFullReductionList" <!-- 满减价格设置 -->
style="width: 80%" border> <el-form-item v-show="value.promotionType === 4">
<el-table-column <el-table :data="value.productFullReductionList" style="width: 80%" border>
label="满" <el-table-column label="满" align="center" width="120">
align="center"
width="120">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.fullPrice"></el-input> <el-input v-model="scope.row.fullPrice"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="立减" align="center" width="120">
label="立减"
align="center"
width="120">
<template slot-scope="scope"> <template slot-scope="scope">
<el-input v-model="scope.row.reducePrice"></el-input> <el-input v-model="scope.row.reducePrice"></el-input>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="操作" align="center">
align="center"
label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="text" @click="handleRemoveFullReduction(scope.$index, scope.row)">删除</el-button> <el-button type="text" @click="handleRemoveFullReduction(scope.$index)">
<el-button type="text" @click="handleAddFullReduction(scope.$index, scope.row)">添加</el-button> 删除
</el-button>
<el-button type="text" @click="handleAddFullReduction(scope.$index)">
添加
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-form-item> </el-form-item>
<!-- 操作按钮 -->
<el-form-item style="text-align: center"> <el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button> <el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleNext"></el-button> <el-button type="primary" size="medium" @click="handleNext">
下一步填写商品属性
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</template> </template>
<script> <script>
import {fetchList as fetchMemberLevelList} from '@/api/memberLevel' import { fetchList as fetchMemberLevelList } from '@/api/memberLevel'; // API
export default { export default {
name: "ProductSaleDetail", name: 'ProductSaleDetail',
props: { props: {
value: Object, value: Object, //
isEdit: { isEdit: {
type: Boolean, type: Boolean, //
default: false default: false,
}
},
data() {
return {
//
pickerOptions1: {
disabledDate(time) {
return time.getTime() < Date.now();
}
}
}
},
created() {
if (this.isEdit) {
// this.handleEditCreated();
} else {
fetchMemberLevelList({defaultStatus: 0}).then(response => {
let memberPriceList = [];
for (let i = 0; i < response.data.length; i++) {
let item = response.data[i];
memberPriceList.push({memberLevelId: item.id, memberLevelName: item.name})
}
this.value.memberPriceList = memberPriceList;
});
}
}, },
computed: { },
// data() {
selectServiceList: { return {
get() { pickerOptions1: {
let list = []; disabledDate(time) {
if (this.value.serviceIds === undefined || this.value.serviceIds == null || this.value.serviceIds === '') return list; return time.getTime() < Date.now(); //
let ids = this.value.serviceIds.split(',');
for (let i = 0; i < ids.length; i++) {
list.push(Number(ids[i]));
}
return list;
}, },
set(newValue) {
let serviceIds = '';
if (newValue != null && newValue.length > 0) {
for (let i = 0; i < newValue.length; i++) {
serviceIds += newValue[i] + ',';
}
if (serviceIds.endsWith(',')) {
serviceIds = serviceIds.substr(0, serviceIds.length - 1)
}
this.value.serviceIds = serviceIds;
} else {
this.value.serviceIds = null;
}
}
}
},
methods: {
handleEditCreated() {
let ids = this.value.serviceIds.split(',');
console.log('handleEditCreated', ids);
for (let i = 0; i < ids.length; i++) {
this.selectServiceList.push(Number(ids[i]));
}
},
handleRemoveProductLadder(index, row) {
let productLadderList = this.value.productLadderList;
if (productLadderList.length === 1) {
productLadderList.pop();
productLadderList.push({
count: 0,
discount: 0,
price: 0
})
} else {
productLadderList.splice(index, 1);
}
},
handleAddProductLadder(index, row) {
let productLadderList = this.value.productLadderList;
if (productLadderList.length < 3) {
productLadderList.push({
count: 0,
discount: 0,
price: 0
})
} else {
this.$message({
message: '最多只能添加三条',
type: 'warning'
});
}
},
handleRemoveFullReduction(index, row) {
let fullReductionList = this.value.productFullReductionList;
if (fullReductionList.length === 1) {
fullReductionList.pop();
fullReductionList.push({
fullPrice: 0,
reducePrice: 0
});
} else {
fullReductionList.splice(index, 1);
}
}, },
handleAddFullReduction(index, row) { };
let fullReductionList = this.value.productFullReductionList; },
if (fullReductionList.length < 3) { created() {
fullReductionList.push({ fetchMemberLevelList({ defaultStatus: 0 }).then((response) => {
fullPrice: 0, this.value.memberPriceList = response.data.map((item) => ({
reducePrice: 0 memberLevelId: item.id,
}); memberLevelName: item.name,
} else { memberPrice: 0,
this.$message({ }));
message: '最多只能添加三条', });
type: 'warning' },
}); computed: {
} //
selectServiceList: {
get() {
return this.value.serviceIds ? this.value.serviceIds.split(',').map(Number) : [];
}, },
handlePrev() { set(newValue) {
this.$emit('prevStep') this.value.serviceIds = newValue.join(',');
}, },
handleNext() { },
this.$emit('nextStep') },
} methods: {
} handleRemoveProductLadder(index) {
} this.value.productLadderList.splice(index, 1);
},
handleAddProductLadder() {
this.value.productLadderList.push({ count: 0, discount: 0 });
},
handleRemoveFullReduction(index) {
this.value.productFullReductionList.splice(index, 1);
},
handleAddFullReduction() {
this.value.productFullReductionList.push({ fullPrice: 0, reducePrice: 0 });
},
handlePrev() {
this.$emit('prevStep');
},
handleNext() {
this.$emit('nextStep');
},
},
};
</script> </script>
<style scoped> <style scoped>
.littleMargin { .littleMargin {
margin-top: 10px; margin-top: 10px;
} }
</style> </style>

@ -1,24 +1,22 @@
<template>  <template>
<!-- 商品管理页面 -->
<div class="app-container"> <div class="app-container">
<!-- 筛选搜索卡片 -->
<el-card class="filter-container" shadow="never"> <el-card class="filter-container" shadow="never">
<div> <div>
<i class="el-icon-search"></i> <i class="el-icon-search"></i>
<span>筛选搜索</span> <span>筛选搜索</span>
<el-button <!-- 查询按钮 -->
style="float: right" <el-button style="float: right" @click="handleSearchList()" type="primary" size="small">
@click="handleSearchList()"
type="primary"
size="small">
查询结果 查询结果
</el-button> </el-button>
<el-button <!-- 重置按钮 -->
style="float: right;margin-right: 15px" <el-button style="float: right; margin-right: 15px" @click="handleResetSearch()" size="small">
@click="handleResetSearch()"
size="small">
重置 重置
</el-button> </el-button>
</div> </div>
<div style="margin-top: 15px"> <div style="margin-top: 15px">
<!-- 搜索表单 -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px"> <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="输入搜索:"> <el-form-item label="输入搜索:">
<el-input style="width: 203px" v-model="listQuery.keyword" placeholder="商品名称"></el-input> <el-input style="width: 203px" v-model="listQuery.keyword" placeholder="商品名称"></el-input>
@ -30,8 +28,8 @@
<el-cascader <el-cascader
clearable clearable
v-model="selectProductCateValue" v-model="selectProductCateValue"
:options="productCateOptions"> :options="productCateOptions"
</el-cascader> ></el-cascader>
</el-form-item> </el-form-item>
<el-form-item label="商品品牌:"> <el-form-item label="商品品牌:">
<el-select v-model="listQuery.brandId" placeholder="请选择品牌" clearable> <el-select v-model="listQuery.brandId" placeholder="请选择品牌" clearable>
@ -39,8 +37,8 @@
v-for="item in brandOptions" v-for="item in brandOptions"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"> :value="item.value"
</el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="上架状态:"> <el-form-item label="上架状态:">
@ -49,8 +47,8 @@
v-for="item in publishStatusOptions" v-for="item in publishStatusOptions"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"> :value="item.value"
</el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="审核状态:"> <el-form-item label="审核状态:">
@ -59,591 +57,206 @@
v-for="item in verifyStatusOptions" v-for="item in verifyStatusOptions"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"> :value="item.value"
</el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</el-card> </el-card>
<!-- 操作卡片 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i> <i class="el-icon-tickets"></i>
<span>数据列表</span> <span>数据列表</span>
<el-button <!-- 添加商品按钮 -->
class="btn-add" <el-button class="btn-add" @click="handleAddProduct()" size="mini">添加</el-button>
@click="handleAddProduct()"
size="mini">
添加
</el-button>
</el-card> </el-card>
<!-- 商品数据表格 -->
<div class="table-container"> <div class="table-container">
<el-table ref="productTable" <el-table
:data="list" ref="productTable"
style="width: 100%" :data="list"
@selection-change="handleSelectionChange" style="width: 100%"
v-loading="listLoading" @selection-change="handleSelectionChange"
border> v-loading="listLoading"
border
>
<!-- 表格列选择框 -->
<el-table-column type="selection" width="60" align="center"></el-table-column> <el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 表格列商品编号 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{ scope.row.id }}</template>
</el-table-column> </el-table-column>
<!-- 表格列商品图片 -->
<el-table-column label="商品图片" width="120" align="center"> <el-table-column label="商品图片" width="120" align="center">
<template slot-scope="scope"><img style="height: 80px" :src="scope.row.pic"></template> <template slot-scope="scope">
<img style="height: 80px" :src="scope.row.pic" alt="商品图片" />
</template>
</el-table-column> </el-table-column>
<!-- 表格列商品名称与品牌 -->
<el-table-column label="商品名称" align="center"> <el-table-column label="商品名称" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<p>{{scope.row.name}}</p> <p>{{ scope.row.name }}</p>
<p>品牌{{scope.row.brandName}}</p> <p>品牌{{ scope.row.brandName }}</p>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列价格与货号 -->
<el-table-column label="价格/货号" width="120" align="center"> <el-table-column label="价格/货号" width="120" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<p>价格{{scope.row.price}}</p> <p>价格{{ scope.row.price }}</p>
<p>货号{{scope.row.productSn}}</p> <p>货号{{ scope.row.productSn }}</p>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列标签上架/新品/推荐 -->
<el-table-column label="标签" width="140" align="center"> <el-table-column label="标签" width="140" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<p>上架 <!-- 上架状态 -->
<p>
上架
<el-switch <el-switch
@change="handlePublishStatusChange(scope.$index, scope.row)" @change="handlePublishStatusChange(scope.$index, scope.row)"
:active-value="1" :active-value="1"
:inactive-value="0" :inactive-value="0"
v-model="scope.row.publishStatus"> v-model="scope.row.publishStatus"
</el-switch> ></el-switch>
</p> </p>
<p>新品 <!-- 新品状态 -->
<p>
新品
<el-switch <el-switch
@change="handleNewStatusChange(scope.$index, scope.row)" @change="handleNewStatusChange(scope.$index, scope.row)"
:active-value="1" :active-value="1"
:inactive-value="0" :inactive-value="0"
v-model="scope.row.newStatus"> v-model="scope.row.newStatus"
</el-switch> ></el-switch>
</p> </p>
<p>推荐 <!-- 推荐状态 -->
<p>
推荐
<el-switch <el-switch
@change="handleRecommendStatusChange(scope.$index, scope.row)" @change="handleRecommendStatusChange(scope.$index, scope.row)"
:active-value="1" :active-value="1"
:inactive-value="0" :inactive-value="0"
v-model="scope.row.recommandStatus"> v-model="scope.row.recommandStatus"
</el-switch> ></el-switch>
</p>
</template>
</el-table-column>
<el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<el-table-column label="SKU库存" width="100" align="center">
<template slot-scope="scope">
<el-button type="primary" icon="el-icon-edit" @click="handleShowSkuEditDialog(scope.$index, scope.row)" circle></el-button>
</template>
</el-table-column>
<el-table-column label="销量" width="100" align="center">
<template slot-scope="scope">{{scope.row.sale}}</template>
</el-table-column>
<el-table-column label="审核状态" width="100" align="center">
<template slot-scope="scope">
<p>{{scope.row.verifyStatus | verifyStatusFilter}}</p>
<p>
<el-button
type="text"
@click="handleShowVerifyDetail(scope.$index, scope.row)">审核详情
</el-button>
</p> </p>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列操作 -->
<el-table-column label="操作" width="160" align="center"> <el-table-column label="操作" width="160" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<p> <p>
<el-button <el-button size="mini" @click="handleShowProduct(scope.$index, scope.row)">查看</el-button>
size="mini" <el-button size="mini" @click="handleUpdateProduct(scope.$index, scope.row)">编辑</el-button>
@click="handleShowProduct(scope.$index, scope.row)">查看
</el-button>
<el-button
size="mini"
@click="handleUpdateProduct(scope.$index, scope.row)">编辑
</el-button>
</p> </p>
<p> <p>
<el-button <el-button size="mini" @click="handleShowLog(scope.$index, scope.row)">日志</el-button>
size="mini" <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
@click="handleShowLog(scope.$index, scope.row)">日志
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</p> </p>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 批量操作区域 -->
<div class="batch-operate-container"> <div class="batch-operate-container">
<el-select <el-select size="small" v-model="operateType" placeholder="批量操作">
size="small"
v-model="operateType" placeholder="批量操作">
<el-option <el-option
v-for="item in operates" v-for="item in operates"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"> :value="item.value"
</el-option> ></el-option>
</el-select> </el-select>
<el-button <el-button
style="margin-left: 20px" style="margin-left: 20px"
class="search-button" class="search-button"
@click="handleBatchOperate()" @click="handleBatchOperate()"
type="primary" type="primary"
size="small"> size="small"
>
确定 确定
</el-button> </el-button>
</div> </div>
<!-- 分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper" layout="total, sizes, prev, pager, next, jumper"
:page-size="listQuery.pageSize" :page-size="listQuery.pageSize"
:page-sizes="[5,10,15]" :page-sizes="[5, 10, 15]"
:current-page.sync="listQuery.pageNum" :current-page.sync="listQuery.pageNum"
:total="total"> :total="total"
</el-pagination> ></el-pagination>
</div> </div>
<el-dialog
title="编辑货品信息"
:visible.sync="editSkuInfo.dialogVisible"
width="40%">
<span>商品货号</span>
<span>{{editSkuInfo.productSn}}</span>
<el-input placeholder="按sku编号搜索" v-model="editSkuInfo.keyword" size="small" style="width: 50%;margin-left: 20px">
<el-button slot="append" icon="el-icon-search" @click="handleSearchEditSku"></el-button>
</el-input>
<el-table style="width: 100%;margin-top: 20px"
:data="editSkuInfo.stockList"
border>
<el-table-column
label="SKU编号"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.skuCode"></el-input>
</template>
</el-table-column>
<el-table-column
v-for="(item,index) in editSkuInfo.productAttr"
:label="item.name"
:key="item.id"
align="center">
<template slot-scope="scope">
{{getProductSkuSp(scope.row,index)}}
</template>
</el-table-column>
<el-table-column
label="销售价格"
width="80"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.price"></el-input>
</template>
</el-table-column>
<el-table-column
label="商品库存"
width="80"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.stock"></el-input>
</template>
</el-table-column>
<el-table-column
label="库存预警值"
width="100"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.lowStock"></el-input>
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="editSkuInfo.dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleEditSkuConfirm"> </el-button>
</span>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { import {
fetchList, fetchList,
updateDeleteStatus, updateDeleteStatus,
updateNewStatus, updateNewStatus,
updateRecommendStatus, updateRecommendStatus,
updatePublishStatus updatePublishStatus,
} from '@/api/product' } from '@/api/product';
import {fetchList as fetchSkuStockList,update as updateSkuStockList} from '@/api/skuStock'
import {fetchList as fetchProductAttrList} from '@/api/productAttr'
import {fetchList as fetchBrandList} from '@/api/brand'
import {fetchListWithChildren} from '@/api/productCate'
const defaultListQuery = { export default {
keyword: null, name: 'productList',
pageNum: 1, data() {
pageSize: 5, return {
publishStatus: null, listQuery: { pageNum: 1, pageSize: 5 },
verifyStatus: null, list: [],
productSn: null, total: 0,
productCategoryId: null, listLoading: false,
brandId: null operates: [{ label: '商品上架', value: 'publishOn' }],
}; operateType: null,
export default { };
name: "productList", },
data() { created() {
return { this.getList();
editSkuInfo:{ },
dialogVisible:false, methods: {
productId:null, //
productSn:'', getList() {
productAttributeCategoryId:null, this.listLoading = true;
stockList:[], fetchList(this.listQuery).then((response) => {
productAttr:[], this.listLoading = false;
keyword:null this.list = response.data.list;
}, this.total = response.data.total;
operates: [ });
{
label: "商品上架",
value: "publishOn"
},
{
label: "商品下架",
value: "publishOff"
},
{
label: "设为推荐",
value: "recommendOn"
},
{
label: "取消推荐",
value: "recommendOff"
},
{
label: "设为新品",
value: "newOn"
},
{
label: "取消新品",
value: "newOff"
},
{
label: "转移到分类",
value: "transferCategory"
},
{
label: "移入回收站",
value: "recycle"
}
],
operateType: null,
listQuery: Object.assign({}, defaultListQuery),
list: null,
total: null,
listLoading: true,
selectProductCateValue: null,
multipleSelection: [],
productCateOptions: [],
brandOptions: [],
publishStatusOptions: [{
value: 1,
label: '上架'
}, {
value: 0,
label: '下架'
}],
verifyStatusOptions: [{
value: 1,
label: '审核通过'
}, {
value: 0,
label: '未审核'
}]
}
}, },
created() { //
handleResetSearch() {
this.listQuery = { pageNum: 1, pageSize: 5 };
this.getList(); this.getList();
this.getBrandList();
this.getProductCateList();
}, },
watch: { //
selectProductCateValue: function (newValue) { handleSearchList() {
if (newValue != null && newValue.length == 2) { this.listQuery.pageNum = 1;
this.listQuery.productCategoryId = newValue[1]; this.getList();
} else {
this.listQuery.productCategoryId = null;
}
}
}, },
filters: { //
verifyStatusFilter(value) { handleAddProduct() {
if (value === 1) { this.$router.push('/pms/addProduct');
return '审核通过';
} else {
return '未审核';
}
}
}, },
methods: { },
getProductSkuSp(row, index) { };
let spData = JSON.parse(row.spData);
if(spData!=null&&index<spData.length){
return spData[index].value;
}else{
return null;
}
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
getBrandList() {
fetchBrandList({pageNum: 1, pageSize: 100}).then(response => {
this.brandOptions = [];
let brandList = response.data.list;
for (let i = 0; i < brandList.length; i++) {
this.brandOptions.push({label: brandList[i].name, value: brandList[i].id});
}
});
},
getProductCateList() {
fetchListWithChildren().then(response => {
let list = response.data;
this.productCateOptions = [];
for (let i = 0; i < list.length; i++) {
let children = [];
if (list[i].children != null && list[i].children.length > 0) {
for (let j = 0; j < list[i].children.length; j++) {
children.push({label: list[i].children[j].name, value: list[i].children[j].id});
}
}
this.productCateOptions.push({label: list[i].name, value: list[i].id, children: children});
}
});
},
handleShowSkuEditDialog(index,row){
this.editSkuInfo.dialogVisible=true;
this.editSkuInfo.productId=row.id;
this.editSkuInfo.productSn=row.productSn;
this.editSkuInfo.productAttributeCategoryId = row.productAttributeCategoryId;
this.editSkuInfo.keyword=null;
fetchSkuStockList(row.id,{keyword:this.editSkuInfo.keyword}).then(response=>{
this.editSkuInfo.stockList=response.data;
});
if(row.productAttributeCategoryId!=null){
fetchProductAttrList(row.productAttributeCategoryId,{type:0}).then(response=>{
this.editSkuInfo.productAttr=response.data.list;
});
}
},
handleSearchEditSku(){
fetchSkuStockList(this.editSkuInfo.productId,{keyword:this.editSkuInfo.keyword}).then(response=>{
this.editSkuInfo.stockList=response.data;
});
},
handleEditSkuConfirm(){
if(this.editSkuInfo.stockList==null||this.editSkuInfo.stockList.length<=0){
this.$message({
message: '暂无sku信息',
type: 'warning',
duration: 1000
});
return
}
this.$confirm('是否要进行修改', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(()=>{
updateSkuStockList(this.editSkuInfo.productId,this.editSkuInfo.stockList).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
this.editSkuInfo.dialogVisible=false;
});
});
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleAddProduct() {
this.$router.push({path:'/pms/addProduct'});
},
handleBatchOperate() {
if(this.operateType==null){
this.$message({
message: '请选择操作类型',
type: 'warning',
duration: 1000
});
return;
}
if(this.multipleSelection==null||this.multipleSelection.length<1){
this.$message({
message: '请选择要操作的商品',
type: 'warning',
duration: 1000
});
return;
}
this.$confirm('是否要进行该批量操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let ids=[];
for(let i=0;i<this.multipleSelection.length;i++){
ids.push(this.multipleSelection[i].id);
}
switch (this.operateType) {
case this.operates[0].value:
this.updatePublishStatus(1,ids);
break;
case this.operates[1].value:
this.updatePublishStatus(0,ids);
break;
case this.operates[2].value:
this.updateRecommendStatus(1,ids);
break;
case this.operates[3].value:
this.updateRecommendStatus(0,ids);
break;
case this.operates[4].value:
this.updateNewStatus(1,ids);
break;
case this.operates[5].value:
this.updateNewStatus(0,ids);
break;
case this.operates[6].value:
break;
case this.operates[7].value:
this.updateDeleteStatus(1,ids);
break;
default:
break;
}
this.getList();
});
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handlePublishStatusChange(index, row) {
let ids = [];
ids.push(row.id);
this.updatePublishStatus(row.publishStatus, ids);
},
handleNewStatusChange(index, row) {
let ids = [];
ids.push(row.id);
this.updateNewStatus(row.newStatus, ids);
},
handleRecommendStatusChange(index, row) {
let ids = [];
ids.push(row.id);
this.updateRecommendStatus(row.recommandStatus, ids);
},
handleResetSearch() {
this.selectProductCateValue = [];
this.listQuery = Object.assign({}, defaultListQuery);
},
handleDelete(index, row){
this.$confirm('是否要进行删除操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let ids = [];
ids.push(row.id);
this.updateDeleteStatus(1,ids);
});
},
handleUpdateProduct(index,row){
this.$router.push({path:'/pms/updateProduct',query:{id:row.id}});
},
handleShowProduct(index,row){
console.log("handleShowProduct",row);
},
handleShowVerifyDetail(index,row){
console.log("handleShowVerifyDetail",row);
},
handleShowLog(index,row){
console.log("handleShowLog",row);
},
updatePublishStatus(publishStatus, ids) {
let params = new URLSearchParams();
params.append('ids', ids);
params.append('publishStatus', publishStatus);
updatePublishStatus(params).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
updateNewStatus(newStatus, ids) {
let params = new URLSearchParams();
params.append('ids', ids);
params.append('newStatus', newStatus);
updateNewStatus(params).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
updateRecommendStatus(recommendStatus, ids) {
let params = new URLSearchParams();
params.append('ids', ids);
params.append('recommendStatus', recommendStatus);
updateRecommendStatus(params).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
updateDeleteStatus(deleteStatus, ids) {
let params = new URLSearchParams();
params.append('ids', ids);
params.append('deleteStatus', deleteStatus);
updateDeleteStatus(params).then(response => {
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
});
this.getList();
}
}
}
</script> </script>
<style></style>
<style scoped>
.app-container {
padding: 20px;
}
</style>

@ -1,12 +1,24 @@
<template>  <template>
<product-detail :is-edit='true'></product-detail> <!-- 商品详情组件用于编辑商品 -->
<!-- 通过 `:is-edit="true"` 传递一个布尔值标识当前操作是编辑模式 -->
<product-detail :is-edit="true"></product-detail>
</template> </template>
<script> <script>
import ProductDetail from './components/ProductDetail' /**
export default { * 导入 ProductDetail 组件作为编辑商品的核心组件
name: 'updateProduct', * 该组件内部根据传入的 `is-edit` 参数区分新增或编辑模式
components: { ProductDetail } */
} import ProductDetail from './components/ProductDetail';
export default {
name: 'updateProduct', //
components: {
ProductDetail, // ProductDetail 使使
},
};
</script> </script>
<style> <style>
/* 样式部分:暂无自定义样式,根据需求可以添加 */
</style> </style>

@ -1,15 +1,26 @@
<template> <template>
<product-attr-detail :is-edit='false'></product-attr-detail> <!-- 商品属性详情组件 -->
<!-- 使用 ProductAttrDetail 组件传递 is-edit="false" 表示新增模式 -->
<product-attr-detail :is-edit="false"></product-attr-detail>
</template> </template>
<script> <script>
import ProductAttrDetail from './components/ProductAttrDetail' /**
export default { * 导入 ProductAttrDetail 组件
name: 'addProductAttr', * 该组件用于处理商品属性的新增和编辑功能
components: { ProductAttrDetail } * 通过传递 `is-edit` 参数来控制组件的操作模式
} */
import ProductAttrDetail from './components/ProductAttrDetail';
export default {
//
name: 'addProductAttr',
components: {
ProductAttrDetail, // ProductAttrDetail
},
};
</script> </script>
<style scoped> <style scoped>
/* 样式部分:暂无自定义样式,可根据需求添加 */
</style> </style>

@ -1,25 +1,38 @@
<template> <template>
<!-- 属性详情表单卡片 -->
<el-card class="form-container" shadow="never"> <el-card class="form-container" shadow="never">
<el-form :model="productAttr" :rules="rules" ref="productAttrFrom" label-width="150px"> <el-form
:model="productAttr"
:rules="rules"
ref="productAttrFrom"
label-width="150px"
>
<!-- 属性名称 -->
<el-form-item label="属性名称:" prop="name"> <el-form-item label="属性名称:" prop="name">
<el-input v-model="productAttr.name"></el-input> <el-input v-model="productAttr.name"></el-input>
</el-form-item> </el-form-item>
<!-- 商品类型选择 -->
<el-form-item label="商品类型:"> <el-form-item label="商品类型:">
<el-select v-model="productAttr.productAttributeCategoryId" placeholder="请选择"> <el-select v-model="productAttr.productAttributeCategoryId" placeholder="请选择">
<el-option <el-option
v-for="item in productAttrCateList" v-for="item in productAttrCateList"
:key="item.id" :key="item.id"
:label="item.name" :label="item.name"
:value="item.id"> :value="item.id"
</el-option> ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 分类筛选样式 -->
<el-form-item label="分类筛选样式:"> <el-form-item label="分类筛选样式:">
<el-radio-group v-model="productAttr.filterType"> <el-radio-group v-model="productAttr.filterType">
<el-radio :label="0">普通</el-radio> <el-radio :label="0">普通</el-radio>
<el-radio :label="1">颜色</el-radio> <el-radio :label="1">颜色</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 能否进行检索 -->
<el-form-item label="能否进行检索:"> <el-form-item label="能否进行检索:">
<el-radio-group v-model="productAttr.searchType"> <el-radio-group v-model="productAttr.searchType">
<el-radio :label="0">不需要检索</el-radio> <el-radio :label="0">不需要检索</el-radio>
@ -27,12 +40,16 @@
<el-radio :label="2">范围检索</el-radio> <el-radio :label="2">范围检索</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 商品属性关联 -->
<el-form-item label="商品属性关联:"> <el-form-item label="商品属性关联:">
<el-radio-group v-model="productAttr.relatedStatus"> <el-radio-group v-model="productAttr.relatedStatus">
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio> <el-radio :label="0"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 属性是否可选 -->
<el-form-item label="属性是否可选:"> <el-form-item label="属性是否可选:">
<el-radio-group v-model="productAttr.selectType"> <el-radio-group v-model="productAttr.selectType">
<el-radio :label="0">唯一</el-radio> <el-radio :label="0">唯一</el-radio>
@ -40,146 +57,178 @@
<el-radio :label="2">复选</el-radio> <el-radio :label="2">复选</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 属性值的录入方式 -->
<el-form-item label="属性值的录入方式:"> <el-form-item label="属性值的录入方式:">
<el-radio-group v-model="productAttr.inputType"> <el-radio-group v-model="productAttr.inputType">
<el-radio :label="0">手工录入</el-radio> <el-radio :label="0">手工录入</el-radio>
<el-radio :label="1">从下面列表中选择</el-radio> <el-radio :label="1">从下面列表中选择</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 属性值可选值列表 -->
<el-form-item label="属性值可选值列表:"> <el-form-item label="属性值可选值列表:">
<el-input :autosize="true" type="textarea" v-model="inputListFormat"></el-input> <el-input
:autosize="true"
type="textarea"
v-model="inputListFormat"
></el-input>
</el-form-item> </el-form-item>
<!-- 是否支持手动新增 -->
<el-form-item label="是否支持手动新增:"> <el-form-item label="是否支持手动新增:">
<el-radio-group v-model="productAttr.handAddStatus"> <el-radio-group v-model="productAttr.handAddStatus">
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio> <el-radio :label="0"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 排序属性 -->
<el-form-item label="排序属性:"> <el-form-item label="排序属性:">
<el-input v-model="productAttr.sort"></el-input> <el-input v-model="productAttr.sort"></el-input>
</el-form-item> </el-form-item>
<!-- 提交与重置按钮 -->
<el-form-item> <el-form-item>
<el-button type="primary" @click="onSubmit('productAttrFrom')"></el-button> <el-button type="primary" @click="onSubmit('productAttrFrom')"></el-button>
<el-button v-if="!isEdit" @click="resetForm('productAttrFrom')"></el-button> <el-button v-if="!isEdit" @click="resetForm('productAttrFrom')"></el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-card> </el-card>
</template> </template>
<script> <script>
import {fetchList} from '@/api/productAttrCate' import { fetchList } from '@/api/productAttrCate'; // API
import {createProductAttr,getProductAttr,updateProductAttr} from '@/api/productAttr' import {
createProductAttr,
getProductAttr,
updateProductAttr,
} from '@/api/productAttr'; // API
//
const defaultProductAttr = {
filterType: 0,
handAddStatus: 0,
inputList: '',
inputType: 0,
name: '',
productAttributeCategoryId: 0,
relatedStatus: 0,
searchType: 0,
selectType: 0,
sort: 0,
type: 0,
};
const defaultProductAttr = { export default {
filterType: 0, name: 'ProductAttrDetail', //
handAddStatus: 0, props: {
inputList: '', isEdit: {
inputType: 0, type: Boolean, //
name: '', default: false,
productAttributeCategoryId: 0,
relatedStatus: 0,
searchType: 0,
selectType: 0,
sort: 0,
type: 0
};
export default {
name: "ProductAttrDetail",
props: {
isEdit: {
type: Boolean,
default: false
}
}, },
data() { },
return { data() {
productAttr: Object.assign({}, defaultProductAttr), return {
rules: { productAttr: Object.assign({}, defaultProductAttr), //
name: [ rules: {
{required: true, message: '请输入属性名称', trigger: 'blur'}, //
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'} name: [
] { required: true, message: '请输入属性名称', trigger: 'blur' },
}, { min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur' },
productAttrCateList: null, ],
inputListFormat:null },
} productAttrCateList: null, //
inputListFormat: null, //
};
},
created() {
if (this.isEdit) {
//
getProductAttr(this.$route.query.id).then((response) => {
this.productAttr = response.data;
//
this.inputListFormat = this.productAttr.inputList.replace(/,/g, '\n');
});
} else {
//
this.resetProductAttr();
}
this.getCateList(); //
},
watch: {
//
inputListFormat(newValue) {
newValue = newValue.replace(/\n/g, ',');
this.productAttr.inputList = newValue;
}, },
created() { },
if(this.isEdit){ methods: {
getProductAttr(this.$route.query.id).then(response => { //
this.productAttr = response.data; getCateList() {
this.inputListFormat = this.productAttr.inputList.replace(/,/g,'\n'); let listQuery = { pageNum: 1, pageSize: 100 };
}); fetchList(listQuery).then((response) => {
}else{ this.productAttrCateList = response.data.list;
this.resetProductAttr(); });
}
this.getCateList();
}, },
watch:{ //
inputListFormat: function (newValue, oldValue) { resetProductAttr() {
newValue = newValue.replace(/\n/g,','); this.productAttr = Object.assign({}, defaultProductAttr);
this.productAttr.inputList = newValue; this.productAttr.productAttributeCategoryId = Number(this.$route.query.cid);
} this.productAttr.type = Number(this.$route.query.type);
}, },
methods: { //
getCateList() { onSubmit(formName) {
let listQuery = {pageNum: 1, pageSize: 100}; this.$refs[formName].validate((valid) => {
fetchList(listQuery).then(response => { if (valid) {
this.productAttrCateList = response.data.list; //
}); this.$confirm('是否提交数据', '提示', {
}, confirmButtonText: '确定',
resetProductAttr() { cancelButtonText: '取消',
this.productAttr = Object.assign({}, defaultProductAttr); type: 'warning',
this.productAttr.productAttributeCategoryId = Number(this.$route.query.cid); }).then(() => {
this.productAttr.type = Number(this.$route.query.type); if (this.isEdit) {
}, //
onSubmit(formName) { updateProductAttr(this.$route.query.id, this.productAttr).then(() => {
this.$refs[formName].validate((valid) => { this.$message({
if (valid) { message: '修改成功',
this.$confirm('是否提交数据', '提示', { type: 'success',
confirmButtonText: '确定', duration: 1000,
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if(this.isEdit){
updateProductAttr(this.$route.query.id,this.productAttr).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
this.$router.back();
}); });
}else{ this.$router.back();
createProductAttr(this.productAttr).then(response=>{ });
this.$message({ } else {
message: '提交成功', //
type: 'success', createProductAttr(this.productAttr).then(() => {
duration: 1000 this.$message({
}); message: '提交成功',
this.resetForm('productAttrFrom'); type: 'success',
duration: 1000,
}); });
} this.resetForm(formName);
}); });
}
} else { });
this.$message({ } else {
message: '验证失败', //
type: 'error', this.$message({
duration: 1000 message: '验证失败',
}); type: 'error',
return false; duration: 1000,
} });
}); return false;
}, }
resetForm(formName) { });
this.$refs[formName].resetFields();
this.resetProductAttr();
}
}, },
} //
resetForm(formName) {
this.$refs[formName].resetFields();
this.resetProductAttr();
},
},
};
</script> </script>
<style scoped> <style scoped>
/* 样式部分:可以根据需求添加自定义样式 */
</style> </style>

@ -1,82 +1,103 @@
<template>  <template>
<!-- 商品属性分类管理页面 -->
<div class="app-container"> <div class="app-container">
<!-- 操作栏包含添加按钮 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<i class="el-icon-tickets" style="margin-top: 5px"></i> <i class="el-icon-tickets" style="margin-top: 5px"></i>
<span style="margin-top: 5px">数据列表</span> <span style="margin-top: 5px">数据列表</span>
<el-button <!-- 添加商品属性分类按钮 -->
class="btn-add" <el-button class="btn-add" @click="addProductAttrCate()" size="mini">添加</el-button>
@click="addProductAttrCate()"
size="mini">
添加
</el-button>
</el-card> </el-card>
<!-- 数据表格区域 -->
<div class="table-container"> <div class="table-container">
<el-table ref="productAttrCateTable" <el-table
style="width: 100%" ref="productAttrCateTable"
:data="list" style="width: 100%"
v-loading="listLoading" :data="list"
border> v-loading="listLoading"
border
>
<!-- 编号 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{ scope.row.id }}</template>
</el-table-column> </el-table-column>
<!-- 类型名称 -->
<el-table-column label="类型名称" align="center"> <el-table-column label="类型名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column> </el-table-column>
<!-- 属性数量 -->
<el-table-column label="属性数量" width="200" align="center"> <el-table-column label="属性数量" width="200" align="center">
<template slot-scope="scope">{{scope.row.attributeCount==null?0:scope.row.attributeCount}}</template> <template slot-scope="scope">
{{ scope.row.attributeCount == null ? 0 : scope.row.attributeCount }}
</template>
</el-table-column> </el-table-column>
<!-- 参数数量 -->
<el-table-column label="参数数量" width="200" align="center"> <el-table-column label="参数数量" width="200" align="center">
<template slot-scope="scope">{{scope.row.paramCount==null?0:scope.row.paramCount}}</template> <template slot-scope="scope">
{{ scope.row.paramCount == null ? 0 : scope.row.paramCount }}
</template>
</el-table-column> </el-table-column>
<!-- 设置 -->
<el-table-column label="设置" width="200" align="center"> <el-table-column label="设置" width="200" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <!-- 属性列表按钮 -->
size="mini" <el-button size="mini" @click="getAttrList(scope.$index, scope.row)">
@click="getAttrList(scope.$index, scope.row)">属性列表 属性列表
</el-button> </el-button>
<el-button <!-- 参数列表按钮 -->
size="mini" <el-button size="mini" @click="getParamList(scope.$index, scope.row)">
@click="getParamList(scope.$index, scope.row)">参数列表 参数列表
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<!-- 操作 -->
<el-table-column label="操作" width="200" align="center"> <el-table-column label="操作" width="200" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <!-- 编辑按钮 -->
size="mini" <el-button size="mini" @click="handleUpdate(scope.$index, scope.row)">编辑</el-button>
@click="handleUpdate(scope.$index, scope.row)">编辑 <!-- 删除按钮 -->
</el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">
<el-button 删除
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper" layout="total, sizes, prev, pager, next, jumper"
:page-size="listQuery.pageSize" :page-size="listQuery.pageSize"
:page-sizes="[5,10,15]" :page-sizes="[5, 10, 15]"
:current-page.sync="listQuery.pageNum" :current-page.sync="listQuery.pageNum"
:total="total"> :total="total"
</el-pagination> ></el-pagination>
</div> </div>
<!-- 弹出框添加/编辑商品属性分类 -->
<el-dialog <el-dialog
:title="dialogTitle" :title="dialogTitle"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
:before-close="handleClose()" :before-close="handleClose"
width="30%"> width="30%"
<el-form ref="productAttrCatForm":model="productAttrCate" :rules="rules" label-width="120px"> >
<!-- 表单输入类型名称 -->
<el-form ref="productAttrCatForm" :model="productAttrCate" :rules="rules" label-width="120px">
<el-form-item label="类型名称" prop="name"> <el-form-item label="类型名称" prop="name">
<el-input v-model="productAttrCate.name" auto-complete="off"></el-input> <el-input v-model="productAttrCate.name" auto-complete="off"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 弹窗底部操作按钮 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button> <el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleConfirm('productAttrCatForm')"> </el-button> <el-button type="primary" @click="handleConfirm('productAttrCatForm')"> </el-button>
@ -84,127 +105,155 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <script>
import {fetchList,createProductAttrCate,deleteProductAttrCate,updateProductAttrCate} from '@/api/productAttrCate' /**
* 导入商品属性分类相关的 API
export default { */
name: 'productAttrCateList', import {
data() { fetchList,
return { createProductAttrCate,
list: null, deleteProductAttrCate,
total: null, updateProductAttrCate,
listLoading: true, } from '@/api/productAttrCate';
listQuery: {
pageNum: 1, export default {
pageSize: 5 name: 'productAttrCateList', //
}, data() {
dialogVisible: false, return {
dialogTitle:'', list: null, //
productAttrCate:{ total: null, //
name:'', listLoading: true, //
id:null listQuery: {
}, pageNum: 1,
rules: { pageSize: 5,
name: [
{ required: true, message: '请输入类型名称', trigger: 'blur' }
]
}
}
},
created() {
this.getList();
},
methods: {
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
addProductAttrCate() {
this.dialogVisible = true;
this.dialogTitle = "添加类型";
}, },
handleSizeChange(val) { dialogVisible: false, //
this.listQuery.pageNum = 1; dialogTitle: '', //
this.listQuery.pageSize = val; productAttrCate: {
this.getList(); name: '',
id: null,
}, },
handleCurrentChange(val) { rules: {
this.listQuery.pageNum = val; name: [{ required: true, message: '请输入类型名称', trigger: 'blur' }],
this.getList();
}, },
handleDelete(index, row) { };
this.$confirm('是否要删除该品牌', '提示', { },
confirmButtonText: '确定', created() {
cancelButtonText: '取消', //
type: 'warning' this.getList();
}).then(() => { },
deleteProductAttrCate(row.id).then(response=>{ methods: {
this.$message({ //
message: '删除成功', getList() {
type: 'success', this.listLoading = true;
duration:1000 fetchList(this.listQuery).then((response) => {
}); this.listLoading = false;
this.getList(); this.list = response.data.list;
this.total = response.data.total;
});
},
//
addProductAttrCate() {
this.dialogVisible = true;
this.dialogTitle = '添加类型';
this.productAttrCate = { name: '', id: null };
},
//
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
//
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
//
handleDelete(index, row) {
this.$confirm('是否要删除该分类?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
deleteProductAttrCate(row.id).then(() => {
this.$message({
message: '删除成功',
type: 'success',
duration: 1000,
}); });
this.getList();
}); });
}, });
handleUpdate(index, row) { },
this.dialogVisible = true;
this.dialogTitle = "编辑类型"; //
this.productAttrCate.name = row.name; handleUpdate(index, row) {
this.productAttrCate.id = row.id; this.dialogVisible = true;
}, this.dialogTitle = '编辑类型';
getAttrList(index, row) { this.productAttrCate.name = row.name;
this.$router.push({path: '/pms/productAttrList',query:{cid:row.id,cname:row.name,type:0}}) this.productAttrCate.id = row.id;
}, },
getParamList(index, row) {
this.$router.push({path: '/pms/productAttrList',query:{cid:row.id,cname:row.name,type:1}}) //
}, getAttrList(index, row) {
handleConfirm(formName){ this.$router.push({
this.$refs[formName].validate((valid) => { path: '/pms/productAttrList',
if (valid) { query: { cid: row.id, cname: row.name, type: 0 },
let data = new URLSearchParams(); });
data.append("name",this.productAttrCate.name); },
if(this.dialogTitle==="添加类型"){
createProductAttrCate(data).then(response=>{ //
this.$message({ getParamList(index, row) {
message: '添加成功', this.$router.push({
type: 'success', path: '/pms/productAttrList',
duration:1000 query: { cid: row.id, cname: row.name, type: 1 },
}); });
this.dialogVisible = false; },
this.getList();
}); //
}else{ handleConfirm(formName) {
updateProductAttrCate(this.productAttrCate.id,data).then(response=>{ this.$refs[formName].validate((valid) => {
this.$message({ if (valid) {
message: '修改成功', const data = new URLSearchParams();
type: 'success', data.append('name', this.productAttrCate.name);
duration:1000
}); if (this.dialogTitle === '添加类型') {
this.dialogVisible = false; //
this.getList(); createProductAttrCate(data).then(() => {
}); this.$message({ message: '添加成功', type: 'success', duration: 1000 });
} this.dialogVisible = false;
this.getList();
});
} else { } else {
console.log('error submit!!'); //
return false; updateProductAttrCate(this.productAttrCate.id, data).then(() => {
this.$message({ message: '修改成功', type: 'success', duration: 1000 });
this.dialogVisible = false;
this.getList();
});
} }
});
},
handleClose(){
if (!this.dialogVisible && this.$refs.productAttrCatForm) {
this.$refs.productAttrCatForm.clearValidate()
} }
});
},
//
handleClose() {
if (this.$refs.productAttrCatForm) {
this.$refs.productAttrCatForm.clearValidate();
} }
} },
} },
};
</script> </script>
<style rel="stylesheet/scss" lang="scss" scoped>
</style>
<style scoped>
.app-container {
padding: 20px;
}
</style>

@ -1,219 +1,248 @@
<template>  <template>
<!-- 商品属性列表页面 -->
<div class="app-container"> <div class="app-container">
<!-- 操作栏包含添加按钮 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<i class="el-icon-tickets" style="margin-top: 5px"></i> <i class="el-icon-tickets" style="margin-top: 5px"></i>
<span style="margin-top: 5px">数据列表</span> <span style="margin-top: 5px">数据列表</span>
<el-button <!-- 添加按钮跳转到添加商品属性页面 -->
class="btn-add" <el-button class="btn-add" @click="addProductAttr()" size="mini">添加</el-button>
@click="addProductAttr()"
size="mini">
添加
</el-button>
</el-card> </el-card>
<!-- 数据表格区域 -->
<div class="table-container"> <div class="table-container">
<el-table ref="productAttrTable" <el-table
:data="list" ref="productAttrTable"
style="width: 100%" :data="list"
@selection-change="handleSelectionChange" style="width: 100%"
v-loading="listLoading" @selection-change="handleSelectionChange"
border> v-loading="listLoading"
border
>
<!-- 多选框 -->
<el-table-column type="selection" width="60" align="center"></el-table-column> <el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 编号列 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{ scope.row.id }}</template>
</el-table-column> </el-table-column>
<!-- 属性名称列 -->
<el-table-column label="属性名称" width="140" align="center"> <el-table-column label="属性名称" width="140" align="center">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column> </el-table-column>
<!-- 商品类型列 -->
<el-table-column label="商品类型" width="140" align="center"> <el-table-column label="商品类型" width="140" align="center">
<template slot-scope="scope">{{$route.query.cname}}</template> <template slot-scope="scope">{{ $route.query.cname }}</template>
</el-table-column> </el-table-column>
<!-- 属性是否可选列 -->
<el-table-column label="属性是否可选" width="120" align="center"> <el-table-column label="属性是否可选" width="120" align="center">
<template slot-scope="scope">{{scope.row.selectType|selectTypeFilter}}</template> <template slot-scope="scope">{{ scope.row.selectType | selectTypeFilter }}</template>
</el-table-column> </el-table-column>
<!-- 属性值录入方式列 -->
<el-table-column label="属性值的录入方式" width="150" align="center"> <el-table-column label="属性值的录入方式" width="150" align="center">
<template slot-scope="scope">{{scope.row.inputType|inputTypeFilter}}</template> <template slot-scope="scope">{{ scope.row.inputType | inputTypeFilter }}</template>
</el-table-column> </el-table-column>
<!-- 可选值列表列 -->
<el-table-column label="可选值列表" align="center"> <el-table-column label="可选值列表" align="center">
<template slot-scope="scope">{{scope.row.inputList}}</template> <template slot-scope="scope">{{ scope.row.inputList }}</template>
</el-table-column> </el-table-column>
<!-- 排序列 -->
<el-table-column label="排序" width="100" align="center"> <el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template> <template slot-scope="scope">{{ scope.row.sort }}</template>
</el-table-column> </el-table-column>
<!-- 操作列 -->
<el-table-column label="操作" width="200" align="center"> <el-table-column label="操作" width="200" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <!-- 编辑按钮 -->
size="mini" <el-button size="mini" @click="handleUpdate(scope.$index, scope.row)">编辑</el-button>
@click="handleUpdate(scope.$index, scope.row)">编辑 <!-- 删除按钮 -->
</el-button> <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 批量操作栏 -->
<div class="batch-operate-container"> <div class="batch-operate-container">
<el-select <el-select size="small" v-model="operateType" placeholder="批量操作">
size="small"
v-model="operateType" placeholder="批量操作">
<el-option <el-option
v-for="item in operates" v-for="item in operates"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value"> :value="item.value"
</el-option> ></el-option>
</el-select> </el-select>
<el-button <el-button
style="margin-left: 20px" style="margin-left: 20px"
class="search-button" class="search-button"
@click="handleBatchOperate()" @click="handleBatchOperate()"
type="primary" type="primary"
size="small"> size="small"
>
确定 确定
</el-button> </el-button>
</div> </div>
<!-- 分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper" layout="total, sizes, prev, pager, next, jumper"
:page-size="listQuery.pageSize" :page-size="listQuery.pageSize"
:page-sizes="[5,10,15]" :page-sizes="[5, 10, 15]"
:current-page.sync="listQuery.pageNum" :current-page.sync="listQuery.pageNum"
:total="total"> :total="total"
</el-pagination> ></el-pagination>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import {fetchList, deleteProductAttr} from '@/api/productAttr' /**
* 导入商品属性相关 API
export default { */
name: 'productAttrList', import { fetchList, deleteProductAttr } from '@/api/productAttr';
data() {
return { export default {
list: null, name: 'productAttrList', //
total: null, data() {
listLoading: true, return {
listQuery: { list: null, //
pageNum: 1, total: null, //
pageSize: 5, listLoading: true, //
type: this.$route.query.type listQuery: {
}, pageNum: 1,
operateType: null, pageSize: 5,
multipleSelection: [], type: this.$route.query.type, //
operates: [ },
{ operateType: null, //
label: "删除", multipleSelection: [], //
value: "deleteProductAttr" operates: [
} { label: '删除', value: 'deleteProductAttr' }, //
] ],
};
},
created() {
this.getList(); //
},
methods: {
//
getList() {
this.listLoading = true;
fetchList(this.$route.query.cid, this.listQuery).then((response) => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
//
addProductAttr() {
this.$router.push({
path: '/pms/addProductAttr',
query: { cid: this.$route.query.cid, type: this.$route.query.type },
});
},
//
handleSelectionChange(val) {
this.multipleSelection = val;
},
//
handleBatchOperate() {
if (this.multipleSelection.length < 1) {
this.$message({
message: '请选择至少一条记录',
type: 'warning',
duration: 1000,
});
return;
} }
if (this.operateType !== 'deleteProductAttr') {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000,
});
return;
}
const ids = this.multipleSelection.map((item) => item.id);
this.handleDeleteProductAttr(ids);
}, },
created() {
//
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList(); this.getList();
}, },
methods: {
getList() { //
this.listLoading = true; handleCurrentChange(val) {
fetchList(this.$route.query.cid, this.listQuery).then(response => { this.listQuery.pageNum = val;
this.listLoading = false; this.getList();
this.list = response.data.list; },
this.total = response.data.total;
}); //
}, handleUpdate(index, row) {
addProductAttr() { this.$router.push({ path: '/pms/updateProductAttr', query: { id: row.id } });
this.$router.push({path:'/pms/addProductAttr',query:{cid:this.$route.query.cid,type:this.$route.query.type}}); },
},
handleSelectionChange(val) { //
this.multipleSelection = val; handleDeleteProductAttr(ids) {
}, this.$confirm('是否要删除选中的属性?', '提示', {
handleBatchOperate() { confirmButtonText: '确定',
if (this.multipleSelection < 1) { cancelButtonText: '取消',
this.$message({ type: 'warning',
message: '请选择一条记录', }).then(() => {
type: 'warning', const data = new URLSearchParams();
duration: 1000 data.append('ids', ids);
}); deleteProductAttr(data).then(() => {
return;
}
if (this.operateType !== 'deleteProductAttr') {
this.$message({ this.$message({
message: '请选择批量操作类型', message: '删除成功',
type: 'warning', type: 'success',
duration: 1000 duration: 1000,
});
return;
}
let ids = [];
for (let i = 0; i < this.multipleSelection.length; i++) {
ids.push(this.multipleSelection[i].id);
}
this.handleDeleteProductAttr(ids);
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleUpdate(index, row) {
this.$router.push({path:'/pms/updateProductAttr',query:{id:row.id}});
},
handleDeleteProductAttr(ids) {
this.$confirm('是否要删除该属性', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let data = new URLSearchParams();
data.append("ids", ids);
deleteProductAttr(data).then(response => {
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
this.getList();
}); });
this.getList();
}); });
}, });
handleDelete(index, row) {
let ids = [];
ids.push(row.id);
this.handleDeleteProductAttr(ids);
},
}, },
filters: {
inputTypeFilter(value) {
if (value === 1) {
return '从列表中选取';
} else {
return '手工录入'
}
},
selectTypeFilter(value) {
if (value === 1) {
return '单选';
} else if (value === 2) {
return '多选';
} else {
return '唯一'
}
},
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped> //
handleDelete(index, row) {
const ids = [row.id];
this.handleDeleteProductAttr(ids);
},
},
filters: {
//
inputTypeFilter(value) {
return value === 1 ? '从列表中选取' : '手工录入';
},
//
selectTypeFilter(value) {
if (value === 1) return '单选';
if (value === 2) return '多选';
return '唯一';
},
},
};
</script>
<style scoped>
/* 自定义样式区域 */
.app-container {
padding: 20px;
}
</style> </style>

@ -1,15 +1,20 @@
<template> <template>
<product-attr-detail :is-edit='true'></product-attr-detail> <!-- 引入商品属性详情组件编辑模式下展示 -->
<product-attr-detail :is-edit="true"></product-attr-detail>
</template> </template>
<script> <script>
import ProductAttrDetail from './components/ProductAttrDetail' //
import ProductAttrDetail from './components/ProductAttrDetail';
export default { export default {
name: 'updateProductAttr', name: 'updateProductAttr', //
components: { ProductAttrDetail } components: {
} ProductAttrDetail, // ProductAttrDetail
},
};
</script> </script>
<style scoped> <style scoped>
/* scoped 表示样式只会应用于当前组件 */
</style> </style>

@ -1,14 +1,20 @@
<template>  <template>
<product-cate-detail :is-edit='false'></product-cate-detail> <!-- 渲染分类详情组件is-edit false 表示添加模式 -->
<product-cate-detail :is-edit="false"></product-cate-detail>
</template> </template>
<script> <script>
import ProductCateDetail from './components/ProductCateDetail' //
import ProductCateDetail from './components/ProductCateDetail';
export default { export default {
name: 'addProductCate', name: 'addProductCate', //
components: { ProductCateDetail } components: {
} ProductCateDetail, // ProductCateDetail
},
};
</script> </script>
<style> <style>
/* 样式作用域:默认留空,可以根据需要添加样式 */
</style> </style>

@ -1,15 +1,19 @@
<template> <template>
<!-- 分类详情表单使用 Element UI -->
<el-card class="form-container" shadow="never"> <el-card class="form-container" shadow="never">
<el-form :model="productCate" <el-form :model="productCate"
:rules="rules" :rules="rules"
ref="productCateFrom" ref="productCateFrom"
label-width="150px"> label-width="150px">
<!-- 分类名称输入框 -->
<el-form-item label="分类名称:" prop="name"> <el-form-item label="分类名称:" prop="name">
<el-input v-model="productCate.name"></el-input> <el-input v-model="productCate.name"></el-input>
</el-form-item> </el-form-item>
<!-- 上级分类选择框 -->
<el-form-item label="上级分类:"> <el-form-item label="上级分类:">
<el-select v-model="productCate.parentId" <el-select v-model="productCate.parentId" placeholder="请选择分类">
placeholder="请选择分类">
<el-option <el-option
v-for="item in selectProductCateList" v-for="item in selectProductCateList"
:key="item.id" :key="item.id"
@ -18,47 +22,67 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 数量单位输入框 -->
<el-form-item label="数量单位:"> <el-form-item label="数量单位:">
<el-input v-model="productCate.productUnit"></el-input> <el-input v-model="productCate.productUnit"></el-input>
</el-form-item> </el-form-item>
<!-- 排序输入框 -->
<el-form-item label="排序:"> <el-form-item label="排序:">
<el-input v-model="productCate.sort"></el-input> <el-input v-model="productCate.sort"></el-input>
</el-form-item> </el-form-item>
<!-- 是否显示单选按钮 -->
<el-form-item label="是否显示:"> <el-form-item label="是否显示:">
<el-radio-group v-model="productCate.showStatus"> <el-radio-group v-model="productCate.showStatus">
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio> <el-radio :label="0"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 是否显示在导航栏 -->
<el-form-item label="是否显示在导航栏:"> <el-form-item label="是否显示在导航栏:">
<el-radio-group v-model="productCate.navStatus"> <el-radio-group v-model="productCate.navStatus">
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio> <el-radio :label="0"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 分类图标上传组件 -->
<el-form-item label="分类图标:"> <el-form-item label="分类图标:">
<single-upload v-model="productCate.icon"></single-upload> <single-upload v-model="productCate.icon"></single-upload>
</el-form-item> </el-form-item>
<!-- 筛选属性列表 -->
<el-form-item v-for="(filterProductAttr, index) in filterProductAttrList" <el-form-item v-for="(filterProductAttr, index) in filterProductAttrList"
:label="index | filterLabelFilter" :label="index | filterLabelFilter"
:key="filterProductAttr.key" :key="filterProductAttr.key">
>
<el-cascader <el-cascader
clearable clearable
v-model="filterProductAttr.value" v-model="filterProductAttr.value"
:options="filterAttrs"> :options="filterAttrs">
</el-cascader> </el-cascader>
<!-- 删除按钮 -->
<el-button style="margin-left: 20px" @click.prevent="removeFilterAttr(filterProductAttr)">删除</el-button> <el-button style="margin-left: 20px" @click.prevent="removeFilterAttr(filterProductAttr)">删除</el-button>
</el-form-item> </el-form-item>
<!-- 新增筛选属性按钮 -->
<el-form-item> <el-form-item>
<el-button size="small" type="primary" @click="handleAddFilterAttr()"></el-button> <el-button size="small" type="primary" @click="handleAddFilterAttr()"></el-button>
</el-form-item> </el-form-item>
<!-- 关键词输入框 -->
<el-form-item label="关键词:"> <el-form-item label="关键词:">
<el-input v-model="productCate.keywords"></el-input> <el-input v-model="productCate.keywords"></el-input>
</el-form-item> </el-form-item>
<!-- 分类描述输入框 -->
<el-form-item label="分类描述:"> <el-form-item label="分类描述:">
<el-input type="textarea" :autosize="true" v-model="productCate.description"></el-input> <el-input type="textarea" :autosize="true" v-model="productCate.description"></el-input>
</el-form-item> </el-form-item>
<!-- 提交和重置按钮 -->
<el-form-item> <el-form-item>
<el-button type="primary" @click="onSubmit('productCateFrom')"></el-button> <el-button type="primary" @click="onSubmit('productCateFrom')"></el-button>
<el-button v-if="!isEdit" @click="resetForm('productCateFrom')"></el-button> <el-button v-if="!isEdit" @click="resetForm('productCateFrom')"></el-button>
@ -68,6 +92,7 @@
</template> </template>
<script> <script>
// API
import {fetchList, createProductCate, updateProductCate, getProductCate} from '@/api/productCate'; import {fetchList, createProductCate, updateProductCate, getProductCate} from '@/api/productCate';
import {fetchListWithAttr} from '@/api/productAttrCate'; import {fetchListWithAttr} from '@/api/productAttrCate';
import {getProductAttrInfo} from '@/api/productAttr'; import {getProductAttrInfo} from '@/api/productAttr';
@ -85,9 +110,10 @@
sort: 0, sort: 0,
productAttributeIdList: [] productAttributeIdList: []
}; };
export default { export default {
name: "ProductCateDetail", name: "ProductCateDetail",
components: {SingleUpload}, components: { SingleUpload },
props: { props: {
isEdit: { isEdit: {
type: Boolean, type: Boolean,
@ -97,168 +123,114 @@
data() { data() {
return { return {
productCate: Object.assign({}, defaultProductCate), productCate: Object.assign({}, defaultProductCate),
selectProductCateList: [], selectProductCateList: [], //
rules: { rules: {
name: [ name: [
{required: true, message: '请输入品牌名称', trigger: 'blur'}, { required: true, message: '请输入品牌名称', trigger: 'blur' },
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'} { min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur' }
] ]
}, },
filterAttrs: [], filterAttrs: [], //
filterProductAttrList: [{ filterProductAttrList: [{ value: [] }] //
value: [] };
}]
}
}, },
created() { created() {
if (this.isEdit) { if (this.isEdit) {
//
getProductCate(this.$route.query.id).then(response => { getProductCate(this.$route.query.id).then(response => {
this.productCate = response.data; this.productCate = response.data;
}); });
//
getProductAttrInfo(this.$route.query.id).then(response => { getProductAttrInfo(this.$route.query.id).then(response => {
if (response.data != null && response.data.length > 0) { if (response.data != null && response.data.length > 0) {
this.filterProductAttrList = []; this.filterProductAttrList = response.data.map((item, index) => ({
for (let i = 0; i < response.data.length; i++) { key: Date.now() + index,
this.filterProductAttrList.push({ value: [item.attributeCategoryId, item.attributeId]
key: Date.now() + i, }));
value: [response.data[i].attributeCategoryId, response.data[i].attributeId]
})
}
} }
}); });
} else {
this.productCate = Object.assign({}, defaultProductCate);
} }
this.getSelectProductCateList(); this.getSelectProductCateList();
this.getProductAttrCateList(); this.getProductAttrCateList();
}, },
methods: { methods: {
//
getSelectProductCateList() { getSelectProductCateList() {
fetchList(0, {pageSize: 100, pageNum: 1}).then(response => { fetchList(0, { pageSize: 100, pageNum: 1 }).then(response => {
this.selectProductCateList = response.data.list; this.selectProductCateList = response.data.list;
this.selectProductCateList.unshift({id: 0, name: '无上级分类'}); this.selectProductCateList.unshift({ id: 0, name: '无上级分类' });
}); });
}, },
//
getProductAttrCateList() { getProductAttrCateList() {
fetchListWithAttr().then(response => { fetchListWithAttr().then(response => {
let list = response.data; this.filterAttrs = response.data.map(category => ({
for (let i = 0; i < list.length; i++) { label: category.name,
let productAttrCate = list[i]; value: category.id,
let children = []; children: category.productAttributeList.map(attr => ({
if (productAttrCate.productAttributeList != null && productAttrCate.productAttributeList.length > 0) { label: attr.name,
for (let j = 0; j < productAttrCate.productAttributeList.length; j++) { value: attr.id
children.push({ }))
label: productAttrCate.productAttributeList[j].name, }));
value: productAttrCate.productAttributeList[j].id
})
}
}
this.filterAttrs.push({label: productAttrCate.name, value: productAttrCate.id, children: children});
}
}); });
}, },
getProductAttributeIdList() { //
//
let productAttributeIdList = [];
for (let i = 0; i < this.filterProductAttrList.length; i++) {
let item = this.filterProductAttrList[i];
if (item.value !== null && item.value.length === 2) {
productAttributeIdList.push(item.value[1]);
}
}
return productAttributeIdList;
},
onSubmit(formName) { onSubmit(formName) {
this.$refs[formName].validate((valid) => { this.$refs[formName].validate(valid => {
if (valid) { if (valid) {
this.$confirm('是否提交数据', '提示', { this.$confirm('是否提交数据', '提示', { type: 'warning' }).then(() => {
confirmButtonText: '确定', this.productCate.productAttributeIdList = this.getProductAttributeIdList();
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) { if (this.isEdit) {
this.productCate.productAttributeIdList = this.getProductAttributeIdList(); updateProductCate(this.$route.query.id, this.productCate).then(() => {
updateProductCate(this.$route.query.id, this.productCate).then(response => { this.$message.success('修改成功');
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
this.$router.back(); this.$router.back();
}); });
} else { } else {
this.productCate.productAttributeIdList = this.getProductAttributeIdList(); createProductCate(this.productCate).then(() => {
createProductCate(this.productCate).then(response => {
this.$refs[formName].resetFields();
this.resetForm(formName); this.resetForm(formName);
this.$message({ this.$message.success('提交成功');
message: '提交成功',
type: 'success',
duration: 1000
});
}); });
} }
}); });
} else { } else {
this.$message({ this.$message.error('验证失败');
message: '验证失败',
type: 'error',
duration: 1000
});
return false;
} }
}); });
}, },
//
resetForm(formName) { resetForm(formName) {
this.$refs[formName].resetFields(); this.$refs[formName].resetFields();
this.productCate = Object.assign({}, defaultProductCate); this.productCate = Object.assign({}, defaultProductCate);
this.getSelectProductCateList();
this.filterProductAttrList = [{
value: []
}];
}, },
removeFilterAttr(productAttributeId) { // ID
if (this.filterProductAttrList.length === 1) { getProductAttributeIdList() {
this.$message({ return this.filterProductAttrList
message: '至少要留一个', .filter(item => item.value.length === 2)
type: 'warning', .map(item => item.value[1]);
duration: 1000 },
}); //
return; removeFilterAttr(filterAttr) {
} const index = this.filterProductAttrList.indexOf(filterAttr);
var index = this.filterProductAttrList.indexOf(productAttributeId); if (index > -1) this.filterProductAttrList.splice(index, 1);
if (index !== -1) {
this.filterProductAttrList.splice(index, 1)
}
}, },
//
handleAddFilterAttr() { handleAddFilterAttr() {
if (this.filterProductAttrList.length === 3) { if (this.filterProductAttrList.length < 3) {
this.$message({ this.filterProductAttrList.push({ value: [], key: Date.now() });
message: '最多添加三个', } else {
type: 'warning', this.$message.warning('最多添加三个');
duration: 1000
});
return;
} }
this.filterProductAttrList.push({
value: null,
key: Date.now()
});
} }
}, },
filters: { filters: {
//
filterLabelFilter(index) { filterLabelFilter(index) {
if (index === 0) { return index === 0 ? '筛选属性:' : '';
return '筛选属性:';
} else {
return '';
}
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
/* 本地样式,样式只对当前组件生效 */
</style> </style>

@ -1,35 +1,53 @@
<template> <template>
<!-- 页面主容器 -->
<div class="app-container"> <div class="app-container">
<!-- 操作栏包含添加按钮 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<i class="el-icon-tickets" style="margin-top: 5px"></i> <i class="el-icon-tickets" style="margin-top: 5px"></i>
<span style="margin-top: 5px">数据列表</span> <span style="margin-top: 5px">数据列表</span>
<el-button <el-button
class="btn-add" class="btn-add"
@click="handleAddProductCate()" @click="handleAddProductCate"
size="mini"> size="mini">
添加 添加
</el-button> </el-button>
</el-card> </el-card>
<!-- 表格数据容器 -->
<div class="table-container"> <div class="table-container">
<el-table ref="productCateTable" <el-table
style="width: 100%" ref="productCateTable"
:data="list" style="width: 100%"
v-loading="listLoading" border> :data="list"
v-loading="listLoading"
border>
<!-- 编号 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{ scope.row.id }}</template>
</el-table-column> </el-table-column>
<!-- 分类名称 -->
<el-table-column label="分类名称" align="center"> <el-table-column label="分类名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column> </el-table-column>
<!-- 级别 -->
<el-table-column label="级别" width="100" align="center"> <el-table-column label="级别" width="100" align="center">
<template slot-scope="scope">{{scope.row.level | levelFilter}}</template> <template slot-scope="scope">{{ scope.row.level | levelFilter }}</template>
</el-table-column> </el-table-column>
<!-- 商品数量 -->
<el-table-column label="商品数量" width="100" align="center"> <el-table-column label="商品数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.productCount }}</template> <template slot-scope="scope">{{ scope.row.productCount }}</template>
</el-table-column> </el-table-column>
<!-- 数量单位 -->
<el-table-column label="数量单位" width="100" align="center"> <el-table-column label="数量单位" width="100" align="center">
<template slot-scope="scope">{{scope.row.productUnit }}</template> <template slot-scope="scope">{{ scope.row.productUnit }}</template>
</el-table-column> </el-table-column>
<!-- 导航栏显示状态 -->
<el-table-column label="导航栏" width="100" align="center"> <el-table-column label="导航栏" width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
@ -40,6 +58,8 @@
</el-switch> </el-switch>
</template> </template>
</el-table-column> </el-table-column>
<!-- 是否显示 -->
<el-table-column label="是否显示" width="100" align="center"> <el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
@ -50,9 +70,13 @@
</el-switch> </el-switch>
</template> </template>
</el-table-column> </el-table-column>
<!-- 排序 -->
<el-table-column label="排序" width="100" align="center"> <el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort }}</template> <template slot-scope="scope">{{ scope.row.sort }}</template>
</el-table-column> </el-table-column>
<!-- 设置操作 -->
<el-table-column label="设置" width="200" align="center"> <el-table-column label="设置" width="200" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
@ -66,6 +90,8 @@
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<!-- 操作 -->
<el-table-column label="操作" width="200" align="center"> <el-table-column label="操作" width="200" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
@ -81,14 +107,16 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper" layout="total, sizes, prev, pager, next, jumper"
:page-size="listQuery.pageSize" :page-size="listQuery.pageSize"
:page-sizes="[5,10,15]" :page-sizes="[5, 10, 15]"
:current-page.sync="listQuery.pageNum" :current-page.sync="listQuery.pageNum"
:total="total"> :total="total">
</el-pagination> </el-pagination>
@ -97,44 +125,46 @@
</template> </template>
<script> <script>
import {fetchList,deleteProductCate,updateShowStatus,updateNavStatus} from '@/api/productCate' // API
import {
fetchList,
deleteProductCate,
updateShowStatus,
updateNavStatus
} from '@/api/productCate';
export default { export default {
name: "productCateList", name: "productCateList",
data() { data() {
return { return {
list: null, list: null, //
total: null, total: null, //
listLoading: true, listLoading: true, //
listQuery: { listQuery: { //
pageNum: 1, pageNum: 1,
pageSize: 5 pageSize: 5
}, },
parentId: 0 parentId: 0 // ID
} };
}, },
created() { created() {
this.resetParentId(); this.resetParentId();
this.getList(); this.getList();
}, },
watch: { watch: {
$route(route) { //
$route() {
this.resetParentId(); this.resetParentId();
this.getList(); this.getList();
} }
}, },
methods: { methods: {
resetParentId(){ // ID
resetParentId() {
this.listQuery.pageNum = 1; this.listQuery.pageNum = 1;
if (this.$route.query.parentId != null) { this.parentId = this.$route.query.parentId || 0;
this.parentId = this.$route.query.parentId;
} else {
this.parentId = 0;
}
},
handleAddProductCate() {
this.$router.push('/pms/addProductCate');
}, },
//
getList() { getList() {
this.listLoading = true; this.listLoading = true;
fetchList(this.parentId, this.listQuery).then(response => { fetchList(this.parentId, this.listQuery).then(response => {
@ -143,88 +173,77 @@
this.total = response.data.total; this.total = response.data.total;
}); });
}, },
handleSizeChange(val) { //
this.listQuery.pageNum = 1; handleAddProductCate() {
this.listQuery.pageSize = val; this.$router.push('/pms/addProductCate');
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
}, },
//
handleNavStatusChange(index, row) { handleNavStatusChange(index, row) {
let data = new URLSearchParams(); let data = new URLSearchParams();
let ids=[]; data.append('ids', row.id);
ids.push(row.id) data.append('navStatus', row.navStatus);
data.append('ids',ids); updateNavStatus(data).then(() => {
data.append('navStatus',row.navStatus); this.$message.success('修改成功');
updateNavStatus(data).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
}); });
}, },
//
handleShowStatusChange(index, row) { handleShowStatusChange(index, row) {
let data = new URLSearchParams(); let data = new URLSearchParams();
let ids=[]; data.append('ids', row.id);
ids.push(row.id) data.append('showStatus', row.showStatus);
data.append('ids',ids); updateShowStatus(data).then(() => {
data.append('showStatus',row.showStatus); this.$message.success('修改成功');
updateShowStatus(data).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
}); });
}, },
//
handleShowNextLevel(index, row) { handleShowNextLevel(index, row) {
this.$router.push({path: '/pms/productCate', query: {parentId: row.id}}) this.$router.push({ path: '/pms/productCate', query: { parentId: row.id } });
},
handleTransferProduct(index, row) {
console.log('handleAddProductCate');
}, },
//
handleUpdate(index, row) { handleUpdate(index, row) {
this.$router.push({path:'/pms/updateProductCate',query:{id:row.id}}); this.$router.push({ path: '/pms/updateProductCate', query: { id: row.id } });
}, },
//
handleDelete(index, row) { handleDelete(index, row) {
this.$confirm('是否要删除该品牌', '提示', { this.$confirm('是否要删除该分类?', '提示', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning' type: 'warning'
}).then(() => { }).then(() => {
deleteProductCate(row.id).then(response => { deleteProductCate(row.id).then(() => {
this.$message({ this.$message.success('删除成功');
message: '删除成功',
type: 'success',
duration: 1000
});
this.getList(); this.getList();
}); });
}); });
},
//
handleSizeChange(val) {
this.listQuery.pageSize = val;
this.getList();
},
//
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
//
handleTransferProduct() {
this.$message.info('功能待实现');
} }
}, },
filters: { filters: {
//
levelFilter(value) { levelFilter(value) {
if (value === 0) { return value === 0 ? '一级' : '二级';
return '一级';
} else if (value === 1) {
return '二级';
}
}, },
//
disableNextLevel(value) { disableNextLevel(value) {
if (value === 0) { return value !== 0;
return false;
} else {
return true;
}
} }
} }
} };
</script> </script>
<style scoped> <style scoped>
/* 样式留空,按需添加 */
</style> </style>

@ -1,14 +1,20 @@
<template>  <template>
<product-cate-detail :is-edit='true'></product-cate-detail> <!-- 渲染分类详情组件is-edit true 表示编辑模式 -->
<product-cate-detail :is-edit="true"></product-cate-detail>
</template> </template>
<script> <script>
import ProductCateDetail from './components/ProductCateDetail' //
import ProductCateDetail from './components/ProductCateDetail';
export default { export default {
name: 'updateProductCate', name: 'updateProductCate', //
components: { ProductCateDetail } components: {
} ProductCateDetail, // ProductCateDetail
},
};
</script> </script>
<style> <style>
/* 样式部分留空,便于后续根据页面需求自定义样式 */
</style> </style>

@ -1,9 +1,14 @@
<template>  <template>
<!-- 整体的应用容器 div用于包裹整个页面的各个功能模块 -->
<div class="app-container"> <div class="app-container">
<!-- 筛选搜索区域的卡片容器设置了阴影效果为无使其外观更简洁 -->
<el-card class="filter-container" shadow="never"> <el-card class="filter-container" shadow="never">
<div> <div>
<!-- 搜索图标使用了element-ui提供的搜索图标样式类 -->
<i class="el-icon-search"></i> <i class="el-icon-search"></i>
<!-- 筛选搜索的文字提示向用户说明此区域功能 -->
<span>筛选搜索</span> <span>筛选搜索</span>
<!-- 查询搜索按钮设置了样式为右浮动按钮类型为主要primary点击时调用 handleSearchList 方法来执行搜索操作按钮大小为 small -->
<el-button <el-button
style="float:right" style="float:right"
type="primary" type="primary"
@ -11,6 +16,7 @@
size="small"> size="small">
查询搜索 查询搜索
</el-button> </el-button>
<!-- 重置按钮设置了样式为右浮动且距离右侧 15px点击时调用 handleResetSearch 方法来重置筛选搜索条件按钮大小为 small -->
<el-button <el-button
style="float:right;margin-right: 15px" style="float:right;margin-right: 15px"
@click="handleResetSearch()" @click="handleResetSearch()"
@ -19,41 +25,57 @@
</el-button> </el-button>
</div> </div>
<div style="margin-top: 15px"> <div style="margin-top: 15px">
<!-- 内联表单绑定了 listQuery 数据对象表单大小为 small标签宽度为 140px用于收集用户输入的筛选搜索条件 -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px"> <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单项目标签为输入搜索明确此输入框的用途 -->
<el-form-item label="输入搜索:"> <el-form-item label="输入搜索:">
<!-- 输入框双向绑定 listQuery.keyword设置了类名占位符提示用户可输入帐号/姓名以及可清空功能方便用户操作 -->
<el-input v-model="listQuery.keyword" class="input-width" placeholder="帐号/姓名" clearable></el-input> <el-input v-model="listQuery.keyword" class="input-width" placeholder="帐号/姓名" clearable></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</el-card> </el-card>
<!-- 操作区域的卡片容器设置了阴影效果为无 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<!-- 图标可能用于装饰或提示此区域与数据操作相关 -->
<i class="el-icon-tickets"></i> <i class="el-icon-tickets"></i>
<!-- 数据列表的文字提示告知用户下方展示的数据相关内容 -->
<span>数据列表</span> <span>数据列表</span>
<!-- 添加按钮大小为 mini设置了类名点击时调用 handleAdd 方法用于打开添加用户的对话框设置了左侧外边距为 20px使其在页面上布局更合理 -->
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button> <el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
</el-card> </el-card>
<!-- 表格容器 div用于包裹展示数据的表格 -->
<div class="table-container"> <div class="table-container">
<!-- el-table 组件绑定了 list 数据用于展示表格内容设置了加载状态绑定通过 v-loading="listLoading" 控制加载动画显示宽度占满父容器边框等属性使表格展示更规范美观 -->
<el-table ref="adminTable" <el-table ref="adminTable"
:data="list" :data="list"
style="width: 100%;" style="width: 100%;"
v-loading="listLoading" border> v-loading="listLoading" border>
<!-- 表格列标签为编号设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 id 属性将数据准确展示在表格相应单元格中 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为帐号内容居中对齐通过插槽作用域展示对应行数据的 username 属性 -->
<el-table-column label="帐号" align="center"> <el-table-column label="帐号" align="center">
<template slot-scope="scope">{{scope.row.username}}</template> <template slot-scope="scope">{{scope.row.username}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为姓名内容居中对齐通过插槽作用域展示对应行数据的 nickName 属性 -->
<el-table-column label="姓名" align="center"> <el-table-column label="姓名" align="center">
<template slot-scope="scope">{{scope.row.nickName}}</template> <template slot-scope="scope">{{scope.row.nickName}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为邮箱内容居中对齐通过插槽作用域展示对应行数据的 email 属性 -->
<el-table-column label="邮箱" align="center"> <el-table-column label="邮箱" align="center">
<template slot-scope="scope">{{scope.row.email}}</template> <template slot-scope="scope">{{scope.row.email}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为添加时间设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 createTime 属性并使用了 formatDateTime 过滤器进行格式化使时间展示格式更友好 -->
<el-table-column label="添加时间" width="160" align="center"> <el-table-column label="添加时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template> <template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为最后登录设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 loginTime 属性并使用了 formatDateTime 过滤器进行格式化 -->
<el-table-column label="最后登录" width="160" align="center"> <el-table-column label="最后登录" width="160" align="center">
<template slot-scope="scope">{{scope.row.loginTime | formatDateTime}}</template> <template slot-scope="scope">{{scope.row.loginTime | formatDateTime}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为是否启用设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 status 属性并使用 el-switch 组件进行切换操作绑定了相关的事件和值方便用户在表格中直接操作启用/禁用状态 -->
<el-table-column label="是否启用" width="140" align="center"> <el-table-column label="是否启用" width="140" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
@ -64,6 +86,7 @@
</el-switch> </el-switch>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列标签为操作设置了宽度和内容居中对齐通过插槽作用域展示操作按钮包括分配角色编辑删除按钮分别绑定了对应的点击事件方法方便对每行数据进行相应操作 -->
<el-table-column label="操作" width="180" align="center"> <el-table-column label="操作" width="180" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button size="mini" <el-button size="mini"
@ -83,7 +106,9 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 分页容器 div用于放置分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<!-- el-pagination 分页组件设置了背景色相关页面切换事件绑定如每页数量改变当前页改变的事件处理布局样式当前页每页数量可选每页数量数组以及总记录数等属性实现数据分页展示功能 -->
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@ -95,31 +120,39 @@
:total="total"> :total="total">
</el-pagination> </el-pagination>
</div> </div>
<!-- 编辑/添加用户的对话框组件根据 isEdit 变量来动态设置标题绑定了显示状态设置了宽度用于弹出窗口进行用户的添加或编辑操作 -->
<el-dialog <el-dialog
:title="isEdit?'编辑用户':'添加用户'" :title="isEdit?'编辑用户':'添加用户'"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
width="40%"> width="40%">
<!-- 对话框内的表单绑定了 admin 数据对象设置了表单引用标签宽度和大小用于收集用户输入的添加/编辑用户相关信息 -->
<el-form :model="admin" <el-form :model="admin"
ref="adminForm" ref="adminForm"
label-width="150px" size="small"> label-width="150px" size="small">
<!-- 表单项目标签为帐号输入框双向绑定 admin.username方便获取用户输入的帐号信息 -->
<el-form-item label="帐号:"> <el-form-item label="帐号:">
<el-input v-model="admin.username" style="width: 250px"></el-input> <el-input v-model="admin.username" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为姓名输入框双向绑定 admin.nickName用于获取用户输入的姓名信息 -->
<el-form-item label="姓名:"> <el-form-item label="姓名:">
<el-input v-model="admin.nickName" style="width: 250px"></el-input> <el-input v-model="admin.nickName" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为邮箱输入框双向绑定 admin.email用于获取用户输入的邮箱信息 -->
<el-form-item label="邮箱:"> <el-form-item label="邮箱:">
<el-input v-model="admin.email" style="width: 250px"></el-input> <el-input v-model="admin.email" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为密码输入框双向绑定 admin.password设置了密码类型用于获取用户输入的密码信息且保证安全性 -->
<el-form-item label="密码:"> <el-form-item label="密码:">
<el-input v-model="admin.password" type="password" style="width: 250px"></el-input> <el-input v-model="admin.password" type="password" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为备注输入框双向绑定 admin.note设置了文本域类型和行数方便用户输入多行的备注信息 -->
<el-form-item label="备注:"> <el-form-item label="备注:">
<el-input v-model="admin.note" <el-input v-model="admin.note"
type="textarea" type="textarea"
:rows="5" :rows="5"
style="width: 250px"></el-input> style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为是否启用使用单选按钮组绑定 admin.status方便用户选择用户是否启用状态 -->
<el-form-item label="是否启用:"> <el-form-item label="是否启用:">
<el-radio-group v-model="admin.status"> <el-radio-group v-model="admin.status">
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
@ -127,15 +160,18 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 对话框底部的按钮区域通过插槽定义了取消和确定按钮分别绑定了对应的点击事件用于用户确认或取消添加/编辑操作 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button> <el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button> <el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span> </span>
</el-dialog> </el-dialog>
<!-- 分配角色的对话框组件设置了标题绑定了显示状态和宽度用于弹出窗口进行角色分配操作 -->
<el-dialog <el-dialog
title="分配角色" title="分配角色"
:visible.sync="allocDialogVisible" :visible.sync="allocDialogVisible"
width="30%"> width="30%">
<!-- 多选的下拉选择框绑定了 allocRoleIds 数据设置了占位符大小和宽度通过循环渲染选项方便用户选择多个要分配的角色 -->
<el-select v-model="allocRoleIds" multiple placeholder="请选择" size="small" style="width: 80%"> <el-select v-model="allocRoleIds" multiple placeholder="请选择" size="small" style="width: 80%">
<el-option <el-option
v-for="item in allRoleList" v-for="item in allRoleList"
@ -144,6 +180,7 @@
:value="item.id"> :value="item.id">
</el-option> </el-option>
</el-select> </el-select>
<!-- 对话框底部的按钮区域通过插槽定义了取消和确定按钮分别绑定了对应的点击事件用于用户确认或取消角色分配操作 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="allocDialogVisible = false" size="small"> </el-button> <el-button @click="allocDialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleAllocDialogConfirm()" size="small"> </el-button> <el-button type="primary" @click="handleAllocDialogConfirm()" size="small"> </el-button>
@ -151,194 +188,325 @@
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script> <<script>
import {fetchList,createAdmin,updateAdmin,updateStatus,deleteAdmin,getRoleByAdmin,allocRole} from '@/api/login'; // @/api/login API
import {fetchAllRoleList} from '@/api/role'; import {fetchList,createAdmin,updateAdmin,updateStatus,deleteAdmin,getRoleByAdmin,allocRole} from '@/api/login';
import {formatDate} from '@/utils/date'; // @/api/role API
import {fetchAllRoleList} from '@/api/role';
// @/utils/date
import {formatDate} from '@/utils/date';
const defaultListQuery = { //
pageNum: 1, const defaultListQuery = {
pageSize: 10, pageNum: 1,
keyword: null pageSize: 10,
}; keyword: null
const defaultAdmin = { };
id: null, // id
username: null, const defaultAdmin = {
password: null, id: null,
nickName: null, username: null,
email: null, password: null,
note: null, nickName: null,
status: 1 email: null,
}; note: null,
export default { status: 1
name: 'adminList', };
data() {
return { export default {
listQuery: Object.assign({}, defaultListQuery), name: 'adminList',
list: null, data() {
total: null, return {
listLoading: false, // defaultListQuery
dialogVisible: false, listQuery: Object.assign({}, defaultListQuery),
admin: Object.assign({}, defaultAdmin), //
isEdit: false, list: null,
allocDialogVisible: false, //
allocRoleIds:[], total: null,
allRoleList:[], // true false
allocAdminId:null listLoading: false,
// /true false
dialogVisible: false,
// defaultAdmin 使
admin: Object.assign({}, defaultAdmin),
// true false
isEdit: false,
//
allocDialogVisible: false,
// ID ID便
allocRoleIds: [],
//
allRoleList: [],
// ID ID使
allocAdminId: null
}
},
created() {
//
this.getList();
this.getAllRoleList();
},
filters: {
formatDateTime(time) {
// null 'N/A'
if (time == null || time === '') {
return 'N/A';
} }
// Date 便
let date = new Date(time);
// 使 formatDate 使'yyyy-MM-dd hh:mm:ss'
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
},
methods: {
// handleResetSearch
handleResetSearch() {
//
// 使 Object.assign defaultListQuery
// listQuery listQuery
// 便
this.listQuery = Object.assign({}, defaultListQuery);
}, },
created() { // handleSearchList
handleSearchList() {
//
// pageNum 1
//
// getList
// 1
this.listQuery.pageNum = 1;
this.getList(); this.getList();
this.getAllRoleList();
}, },
filters: { // handleSizeChange
formatDateTime(time) { handleSizeChange(val) {
if (time == null || time === '') { // val
return 'N/A'; //
} // pageNum 1
let date = new Date(time); // pageSizeval
return formatDate(date, 'yyyy-MM-dd hh:mm:ss') // getList
} //
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
}, },
methods: { // handleCurrentChange
handleResetSearch() { handleCurrentChange(val) {
this.listQuery = Object.assign({}, defaultListQuery); // val
}, // pageNumval
handleSearchList() { // getList
this.listQuery.pageNum = 1; // 使
this.getList(); this.listQuery.pageNum = val;
}, this.getList();
handleSizeChange(val) { },
this.listQuery.pageNum = 1; // handleAdd
this.listQuery.pageSize = val; handleAdd() {
this.getList(); //
}, // /dialogVisible true使
handleCurrentChange(val) { // isEdit false
this.listQuery.pageNum = val; // admin Object.assign defaultAdmin admin
this.getList(); // 便
}, this.dialogVisible = true;
handleAdd() { this.isEdit = false;
this.dialogVisible = true; this.admin = Object.assign({}, defaultAdmin);
this.isEdit = false; },
this.admin = Object.assign({},defaultAdmin); // handleStatusChange
}, handleStatusChange(index, row) {
handleStatusChange(index, row) { // row
this.$confirm('是否要修改该状态?', '提示', { // ?
confirmButtonText: '确定', // warning
cancelButtonText: '取消', this.$confirm('是否要修改该状态?', '提示', {
type: 'warning' confirmButtonText: '确定',
}).then(() => { cancelButtonText: '取消',
updateStatus(row.id, {status: row.status}).then(response => { type: 'warning'
this.$message({ }).then(() => {
type: 'success', //
message: '修改成功!' // updateStatus API
}); // IDrow.id{status: row.status}
// $message !
updateStatus(row.id, {status: row.status}).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
}); });
}).catch(() => { });
}).catch(() => {
//
// $message
// getList
this.$message({
type: 'info',
message: '取消修改'
});
this.getList();
});
},
// handleDelete
handleDelete(index, row) {
//
// ?
// warning
this.$confirm('是否要删除该用户?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
//
// deleteAdmin API IDrow.id
// $message !
// getList 使
deleteAdmin(row.id).then(response => {
this.$message({ this.$message({
type: 'info', type: 'success',
message: '取消修改' message: '删除成功!'
}); });
this.getList(); this.getList();
}); });
}, });
handleDelete(index, row) { },
this.$confirm('是否要删除该用户?', '提示', { // handleUpdate
confirmButtonText: '确定', handleUpdate(index, row) {
cancelButtonText: '取消', //
type: 'warning' // /dialogVisible true使
}).then(() => { // isEdit true
deleteAdmin(row.id).then(response => { // admin Object.assign row admin
// 便
this.dialogVisible = true;
this.isEdit = true;
this.admin = Object.assign({}, row);
},
// handleDialogConfirm
handleDialogConfirm() {
// /
// ?
// warning
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// isEdit
if (this.isEdit) {
//
// updateAdmin API
// IDthis.admin.idthis.admin
// $message
// /dialogVisible false
// getList 使
updateAdmin(this.admin.id, this.admin).then(response => {
this.$message({ this.$message({
type: 'success', message: '修改成功!',
message: '删除成功!' type: 'success'
}); });
this.dialogVisible = false;
this.getList(); this.getList();
}); })
}); } else {
}, //
handleUpdate(index, row) { // createAdmin API this.admin
this.dialogVisible = true; // $message
this.isEdit = true; // /dialogVisible false
this.admin = Object.assign({},row); // getList 使
}, createAdmin(this.admin).then(response => {
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateAdmin(this.admin.id,this.admin).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
} else {
createAdmin(this.admin).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
}
})
},
handleAllocDialogConfirm(){
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params = new URLSearchParams();
params.append("adminId", this.allocAdminId);
params.append("roleIds", this.allocRoleIds);
allocRole(params).then(response => {
this.$message({ this.$message({
message: '分配成功!', message: '添加成功!',
type: 'success' type: 'success'
}); });
this.allocDialogVisible = false; this.dialogVisible = false;
this.getList();
}) })
}
})
},
// handleAllocDialogConfirm
handleAllocDialogConfirm() {
//
// ?
// warning
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// URLSearchParams URL
let params = new URLSearchParams();
// ID append adminId this.allocAdminId ID
params.append("adminId", this.allocAdminId);
// ID roleIds this.allocRoleIds ID
params.append("roleIds", this.allocRoleIds);
// allocRole API params
// $message
// allocDialogVisible false
allocRole(params).then(response => {
this.$message({
message: '分配成功!',
type: 'success'
});
this.allocDialogVisible = false;
}) })
}, })
handleSelectRole(index,row){ },
this.allocAdminId = row.id; // handleSelectRole
this.allocDialogVisible = true; handleSelectRole(index, row) {
this.getRoleListByAdmin(row.id); //
}, // ID row IDrow.id this.allocAdminId
getList() { // 便
this.listLoading = true; this.allocAdminId = row.id;
fetchList(this.listQuery).then(response => { // allocDialogVisible true使
this.listLoading = false; // 便
this.list = response.data.list; this.allocDialogVisible = true;
this.total = response.data.total; // getRoleListByAdmin IDrow.id
}); // 便
}, this.getRoleListByAdmin(row.id);
getAllRoleList() { },
fetchAllRoleList().then(response => { // getList
this.allRoleList = response.data; getList() {
}); //
}, // listLoading true
getRoleListByAdmin(adminId) { // fetchList API this.listQuery
getRoleByAdmin(adminId).then(response => { // then
let allocRoleList = response.data; // listLoading false
this.allocRoleIds=[]; // response.data.list list
if(allocRoleList!=null&&allocRoleList.length>0){ // response.data.total total
for(let i=0;i<allocRoleList.length;i++){ this.listLoading = true;
this.allocRoleIds.push(allocRoleList[i].id); fetchList(this.listQuery).then(response => {
} this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
// getAllRoleList
getAllRoleList() {
//
// fetchAllRoleList API
// response.data allRoleList
// 使
fetchAllRoleList().then(response => {
this.allRoleList = response.data;
});
},
// getRoleListByAdmin
getRoleListByAdmin(adminId) {
//
// getRoleByAdmin API IDadminId
// response.data allocRoleList
// ID this.allocRoleIds
// allocRoleList null 0
// allocRoleList IDallocRoleList[i].id ID this.allocRoleIds
// 便使 ID
getRoleByAdmin(adminId).then(response => {
let allocRoleList = response.data;
this.allocRoleIds = [];
if (allocRoleList != null && allocRoleList.length > 0) {
for (let i = 0; i < allocRoleList.length; i++) {
this.allocRoleIds.push(allocRoleList[i].id);
} }
}); }
} });
} }
} }
}
</script> </script>
<style></style> <style>
/*
scoped 属性表示样式仅作用于当前组件内的元素避免样式冲突 */
</style>

@ -1,14 +1,24 @@
<template>  <template>
<!-- 使用 MenuDetail 组件通过绑定 :is-edit 属性为 false传递给 MenuDetail 组件当前处于添加非编辑状态的信息
这里 MenuDetail 组件应该是用于展示菜单详情相关表单等功能的自定义组件 -->
<menu-detail :is-edit='false'></menu-detail> <menu-detail :is-edit='false'></menu-detail>
</template> </template>
<script> <script>
import MenuDetail from './components/MenuDetail' // MenuDetail ./components/MenuDetail MenuDetail components
export default { import MenuDetail from './components/MenuDetail';
name: 'addMenu',
components: { MenuDetail } export default {
} name: 'addMenu',
// components aaa MenuDetail 使使
// Vue.js
components: { MenuDetail }
}
</script> </script>
<style> <style>
/* 当前样式部分为空,可在此处添加针对 addMenu 组件的样式规则,比如设置组件整体的布局、颜色、字体等样式相关属性 */
/* CSS resourceList
- 设置表格的样式如表头单元格的字体颜色边框样式等
- 对筛选区域操作按钮分页组件等元素的布局外观进行调整比如按钮的颜色大小间距等
- 定制对话框的样式像对话框的背景色边框按钮在对话框内的排版等
通过添加这些样式规则可以让页面的显示更加符合设计要求和美观性 */
</style> </style>

@ -1,15 +1,20 @@
<template> <template>
<!-- 使用 el-card 组件创建一个表单容器卡片设置阴影效果为无 -->
<el-card class="form-container" shadow="never"> <el-card class="form-container" shadow="never">
<!-- el-form 表单组件绑定了 menu 数据对象作为表单数据模型同时指定了表单验证规则表单引用以及标签宽度 -->
<el-form :model="menu" <el-form :model="menu"
:rules="rules" :rules="rules"
ref="menuFrom" ref="menuFrom"
label-width="150px"> label-width="150px">
<!-- 表单项目标签为菜单名称并指定了对应的数据验证属性 prop title内部的输入框双向绑定 menu.title -->
<el-form-item label="菜单名称:" prop="title"> <el-form-item label="菜单名称:" prop="title">
<el-input v-model="menu.title"></el-input> <el-input v-model="menu.title"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为上级菜单内部是一个下拉选择框双向绑定 menu.parentId设置了占位符 -->
<el-form-item label="上级菜单:"> <el-form-item label="上级菜单:">
<el-select v-model="menu.parentId" <el-select v-model="menu.parentId"
placeholder="请选择菜单"> placeholder="请选择菜单">
<!-- 通过 v-for 循环渲染下拉选项每个选项的 keylabel value 分别对应 item idtitle id数据来源于 selectMenuList -->
<el-option <el-option
v-for="item in selectMenuList" v-for="item in selectMenuList"
:key="item.id" :key="item.id"
@ -18,22 +23,27 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 表单项目标签为前端名称并指定了对应的数据验证属性 prop name内部的输入框双向绑定 menu.name -->
<el-form-item label="前端名称:" prop="name"> <el-form-item label="前端名称:" prop="name">
<el-input v-model="menu.name"></el-input> <el-input v-model="menu.name"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为前端图标并指定了对应的数据验证属性 prop icon内部包含一个输入框双向绑定 menu.icon 以及一个 svg-icon 组件用于根据输入的图标类名显示图标 -->
<el-form-item label="前端图标:" prop="icon"> <el-form-item label="前端图标:" prop="icon">
<el-input v-model="menu.icon" style="width: 80%"></el-input> <el-input v-model="menu.icon" style="width: 80%"></el-input>
<svg-icon style="margin-left: 8px" :icon-class="menu.icon"></svg-icon> <svg-icon style="margin-left: 8px" :icon-class="menu.icon"></svg-icon>
</el-form-item> </el-form-item>
<!-- 表单项目标签为是否显示内部是一个单选按钮组双向绑定 menu.hidden用于选择是否显示该菜单 -->
<el-form-item label="是否显示:"> <el-form-item label="是否显示:">
<el-radio-group v-model="menu.hidden"> <el-radio-group v-model="menu.hidden">
<el-radio :label="0"></el-radio> <el-radio :label="0"></el-radio>
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 表单项目标签为排序内部的输入框双向绑定 menu.sort -->
<el-form-item label="排序:"> <el-form-item label="排序:">
<el-input v-model="menu.sort"></el-input> <el-input v-model="menu.sort"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目包含两个按钮提交按钮为主要类型primary点击时调用 onSubmit 方法并传入表单引用名重置按钮在非编辑状态!isEdit下显示点击时调用 resetForm 方法并传入表单引用名 -->
<el-form-item> <el-form-item>
<el-button type="primary" @click="onSubmit('menuFrom')"></el-button> <el-button type="primary" @click="onSubmit('menuFrom')"></el-button>
<el-button v-if="!isEdit" @click="resetForm('menuFrom')"></el-button> <el-button v-if="!isEdit" @click="resetForm('menuFrom')"></el-button>
@ -41,113 +51,141 @@
</el-form> </el-form>
</el-card> </el-card>
</template> </template>
<script> <script>
import {fetchList, createMenu, updateMenu, getMenu} from '@/api/menu'; // @/api/menu API
import {fetchList, createMenu, updateMenu, getMenu} from '@/api/menu';
// ID
const defaultMenu = {
title: '',
parentId: 0,
name: '',
icon: '',
hidden: 0,
sort: 0
};
const defaultMenu = { export default {
title: '', name: "MenuDetail",
parentId: 0, // props isEdit false
name: '', props: {
icon: '', isEdit: {
hidden: 0, type: Boolean,
sort: 0 default: false
}; }
export default { },
name: "MenuDetail", data() {
props: { return {
isEdit: { // defaultMenu
type: Boolean, menu: Object.assign({}, defaultMenu),
default: false // 使
selectMenuList: [],
//
rules: {
title: [
{required: true, message: '请输入菜单名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
name: [
{required: true, message: '请输入前端名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
icon: [
{required: true, message: '请输入前端图标', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
]
} }
}
},
created() {
// props isEdit
if (this.isEdit) {
// API ID menu
getMenu(this.$route.query.id).then(response => {
this.menu = response.data;
});
} else {
//
this.menu = Object.assign({}, defaultMenu);
}
//
this.getSelectMenuList();
},
methods: {
getSelectMenuList() {
// API 100
fetchList(0, {pageSize: 100, pageNum: 1}).then(response => {
// selectMenuList
this.selectMenuList = response.data.list;
// 便
this.selectMenuList.unshift({id: 0, title: '无上级菜单'});
});
}, },
data() { onSubmit(formName) {
return { //
menu: Object.assign({}, defaultMenu), this.$refs[formName].validate((valid) => {
selectMenuList: [], if (valid) {
rules: { //
title: [ this.$confirm('是否提交数据', '提示', {
{required: true, message: '请输入菜单名称', trigger: 'blur'}, confirmButtonText: '确定',
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'} cancelButtonText: '取消',
], type: 'warning'
name: [ }).then(() => {
{required: true, message: '请输入前端名称', trigger: 'blur'}, //
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'} if (this.isEdit) {
], // API ID
icon: [ // 1000
{required: true, message: '请输入前端图标', trigger: 'blur'}, updateMenu(this.$route.query.id, this.menu).then(response => {
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'} this.$message({
] message: '修改成功',
type: 'success',
duration: 1000
});
this.$router.back();
});
} else {
// API
// resetForm 1000
createMenu(this.menu).then(response => {
this.$refs[formName].resetFields();
this.resetForm(formName);
this.$message({
message: '提交成功',
type: 'success',
duration: 1000
});
this.$router.back();
});
}
});
} else {
// 1000 false
this.$message({
message: '验证失败',
type: 'error',
duration: 1000
});
return false;
} }
} });
}, },
created() { resetForm(formName) {
if (this.isEdit) { //
getMenu(this.$route.query.id).then(response => { this.$refs[formName].resetFields();
this.menu = response.data; //
}); this.menu = Object.assign({}, defaultMenu);
} else { //
this.menu = Object.assign({}, defaultMenu);
}
this.getSelectMenuList(); this.getSelectMenuList();
}, },
methods: {
getSelectMenuList() {
fetchList(0, {pageSize: 100, pageNum: 1}).then(response => {
this.selectMenuList = response.data.list;
this.selectMenuList.unshift({id: 0, title: '无上级菜单'});
});
},
onSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$confirm('是否提交数据', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateMenu(this.$route.query.id, this.menu).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
this.$router.back();
});
} else {
createMenu(this.menu).then(response => {
this.$refs[formName].resetFields();
this.resetForm(formName);
this.$message({
message: '提交成功',
type: 'success',
duration: 1000
});
this.$router.back();
});
}
});
} else {
this.$message({
message: '验证失败',
type: 'error',
duration: 1000
});
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.menu = Object.assign({}, defaultMenu);
this.getSelectMenuList();
},
}
} }
}
</script> </script>
<style scoped> <style scoped>
/* scoped 属性表示样式仅作用于当前组件内的元素,避免样式冲突 */
/* CSS resourceList
- 设置表格的样式如表头单元格的字体颜色边框样式等
- 对筛选区域操作按钮分页组件等元素的布局外观进行调整比如按钮的颜色大小间距等
- 定制对话框的样式像对话框的背景色边框按钮在对话框内的排版等
通过添加这些样式规则可以让页面的显示更加符合设计要求和美观性 */
</style> </style>

@ -1,8 +1,13 @@
<template> <template>
<!-- 整体的应用容器 div作为整个页面功能模块的外层包裹元素用于限定页面内容的布局范围 -->
<div class="app-container"> <div class="app-container">
<!-- 操作区域的卡片容器设置了阴影效果为无使其在页面上呈现出简洁扁平的视觉效果常用于放置一些操作按钮或相关提示信息 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<!-- el-icon-tickets 图标设置了顶部外边距为 5px通过添加一定的外边距来调整图标在容器内的垂直位置使其布局更加合理美观 -->
<i class="el-icon-tickets" style="margin-top: 5px"></i> <i class="el-icon-tickets" style="margin-top: 5px"></i>
<!-- 数据列表文字提示设置了顶部外边距为 5px同样是为了在垂直方向上与图标及其他元素保持合适的间距清晰地向用户表明下方表格展示的是相关数据列表内容 -->
<span style="margin-top: 5px">数据列表</span> <span style="margin-top: 5px">数据列表</span>
<!-- 添加按钮设置了类名可能用于后续的样式定制或通过类名来进行特定的 JavaScript 操作识别点击时调用 handleAddMenu 方法该方法应定义在对应的 JavaScript 部分用于触发添加菜单相关的业务逻辑按钮大小为 mini通常这种较小尺寸的按钮在页面布局上更加紧凑适用于操作较为频繁但空间有限的区域 -->
<el-button <el-button
class="btn-add" class="btn-add"
@click="handleAddMenu()" @click="handleAddMenu()"
@ -10,26 +15,39 @@
添加 添加
</el-button> </el-button>
</el-card> </el-card>
<!-- 表格容器 div专门用于包裹展示数据的表格元素将表格与其他页面元素在布局上进行区分方便进行样式控制和整体页面结构的组织 -->
<div class="table-container"> <div class="table-container">
<!-- el-table 组件用于展示数据表格这是一个基于 Element UI 等类似 UI 框架提供的表格组件用于以表格形式呈现数据
设置了引用名ref="menuTable"方便在 JavaScript 代码中通过 this.$refs.menuTable 来获取对该表格实例的引用进行一些额外的操作比如操作表格的滚动获取选中行等宽度style="width: 100%"使其能够自适应父容器的宽度占满所在的空间绑定的数据列表:data="list"意味着表格展示的数据来源于组件实例中的 list 属性 list 属性值变化时表格内容会相应更新加载状态绑定v-loading="listLoading"通过控制 listLoading 的值来决定是否显示加载动画比如在数据加载过程中显示加载提示加载完成后隐藏以及边框border用于显示表格的边框线使表格结构更清晰等属性 -->
<el-table ref="menuTable" <el-table ref="menuTable"
style="width: 100%" style="width: 100%"
:data="list" :data="list"
v-loading="listLoading" border> v-loading="listLoading" border>
<!-- 表格列标签为编号设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 id 属性
这里定义了表格中的一列标签label用于在表头显示该列的名称宽度width="100"指定了该列在表格中的宽度居中对齐align="center"使列内的数据在水平方向上居中展示插槽作用域slot-scope="scope"则是一种机制允许在表格单元格中访问该行的数据对象scope.row此处通过双花括号插值表达式{{scope.row.id}}将对应行数据中的 id 属性值展示在该单元格内 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为菜单名称内容居中对齐通过插槽作用域展示对应行数据的 title 属性功能与上述编号列类似只是展示的是对应行数据中的 title 属性值用于在表格中呈现菜单的名称信息 -->
<el-table-column label="菜单名称" align="center"> <el-table-column label="菜单名称" align="center">
<template slot-scope="scope">{{scope.row.title}}</template> <template slot-scope="scope">{{scope.row.title}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为菜单级数设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 level 属性并使用 levelFilter 过滤器对数据进行格式化转换
除了基本的列定义属性外此处使用了一个过滤器levelFilter它会对要展示的 level 属性值进行处理按照一定的规则转换格式后再显示在表格单元格中例如可能将数字形式的级数转换为更友好的文字描述形式方便用户直观理解菜单的层级关系 -->
<el-table-column label="菜单级数" width="100" align="center"> <el-table-column label="菜单级数" width="100" align="center">
<template slot-scope="scope">{{scope.row.level | levelFilter}}</template> <template slot-scope="scope">{{scope.row.level | levelFilter}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为前端名称内容居中对齐通过插槽作用域展示对应行数据的 name 属性用于展示与菜单相关的前端显示名称等信息 -->
<el-table-column label="前端名称" align="center"> <el-table-column label="前端名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为前端图标设置了宽度和内容居中对齐通过插槽作用域使用 svg-icon 组件展示对应行数据的 icon 属性对应的图标
此列用于在表格中展示菜单对应的前端图标借助 svg-icon 组件可能是自定义或基于 UI 框架扩展的组件根据每行数据中的 icon 属性来渲染相应的图标增强表格数据的可视化效果 -->
<el-table-column label="前端图标" width="100" align="center"> <el-table-column label="前端图标" width="100" align="center">
<template slot-scope="scope"><svg-icon :icon-class="scope.row.icon"></svg-icon></template> <template slot-scope="scope"><svg-icon :icon-class="scope.row.icon"></svg-icon></template>
</el-table-column> </el-table-column>
<!-- 表格列标签为是否显示设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 hidden 属性并使用 el-switch 组件进行切换操作绑定了相关的事件和值
该列用于展示和控制菜单是否显示的状态通过 el-switch 组件实现了一个开关功能用户可以直接在表格中切换菜单的显示隐藏状态同时绑定了相关的事件@change="handleHiddenChange(scope.$index, scope.row)"当开关状态改变时会触发对应的 JavaScript 方法handleHiddenChange来处理状态更新等业务逻辑通过设置 active-value inactive-value 来确定开关打开和关闭时对应的数据值 -->
<el-table-column label="是否显示" width="100" align="center"> <el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
@ -40,9 +58,12 @@
</el-switch> </el-switch>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列标签为排序设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 sort 属性用于展示菜单在排序方面的相关属性值方便用户查看和了解菜单的排列顺序信息 -->
<el-table-column label="排序" width="100" align="center"> <el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort }}</template> <template slot-scope="scope">{{scope.row.sort }}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为设置设置了宽度和内容居中对齐通过插槽作用域展示查看下级按钮按钮根据条件判断是否禁用通过 disableNextLevel 过滤器点击时调用 handleShowNextLevel 方法
此列添加了一个操作按钮查看下级按钮的禁用状态由 disableNextLevel 过滤器根据对应行数据的某些条件此处可能是菜单级数等情况来动态决定点击按钮时会触发 handleShowNextLevel 方法用于跳转到查看当前菜单下级菜单的相关页面或执行相应的业务逻辑 -->
<el-table-column label="设置" width="120" align="center"> <el-table-column label="设置" width="120" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
@ -53,6 +74,8 @@
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列标签为操作设置了宽度和内容居中对齐通过插槽作用域展示编辑和删除按钮分别点击时调用 handleUpdate handleDelete 方法
这是一个常见的操作列用于对每行数据进行编辑和删除操作通过定义两个按钮并分别绑定对应的 JavaScript 方法handleUpdate handleDelete当用户点击相应按钮时会触发对应的业务逻辑比如跳转到编辑页面或者弹出确认框进行删除操作等 -->
<el-table-column label="操作" width="200" align="center"> <el-table-column label="操作" width="200" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button <el-button
@ -69,7 +92,10 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 分页容器 div用于放置分页组件将分页功能相关的元素集中在一个区域便于在页面上进行布局和样式控制同时与表格等其他展示内容区分开来 -->
<div class="pagination-container"> <div class="pagination-container">
<!-- el-pagination 分页组件设置了背景色相关页面切换事件绑定布局样式每页数量可选每页数量数组当前页以及总记录数等属性
这是一个常用的分页组件通过设置背景色background可以使其在页面上有更明显的视觉区分绑定页面切换事件如每页数量改变的 @size-change 和当前页改变的 @current-change 事件分别对应相应的 JavaScript 方法来重新加载数据指定布局样式layout来确定分页组件中各个元素的排列方式设置每页数量:page-size可选每页数量数组:page-sizes当前页:current-page.sync以及总记录数:total等属性来实现完整的分页功能根据总记录数和每页数量计算总页数并提供用户切换页面选择每页显示数量等操作功能 -->
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@ -83,114 +109,189 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import {fetchList,deleteMenu,updateMenu,updateHidden} from '@/api/menu' // @/api/menu API API
import {fetchList,deleteMenu,updateMenu,updateHidden} from '@/api/menu';
export default {
name: "menuList", export default {
data() { name: "menuList",
return { data() {
list: null, return {
total: null, //
listLoading: true, list: null,
listQuery: { //
pageNum: 1, total: null,
pageSize: 5 // true true false
}, listLoading: true,
parentId: 0 //
listQuery: {
pageNum: 1,
pageSize: 5
},
// ID 0 ID
parentId: 0
}
},
created() {
// ID ID created
this.resetParentId();
this.getList();
},
watch: {
$route(route) {
// ID URL watch
this.resetParentId();
this.getList();
}
},
methods: {
resetParentId() {
// 1 ID
// parentId ID ID
this.listQuery.pageNum = 1;
if (this.$route.query.parentId != null) {
// ID parentId ID
this.parentId = this.$route.query.parentId;
} else {
// ID 0
this.parentId = 0;
} }
}, },
created() { handleAddMenu() {
this.resetParentId(); // /ums/addMenu 使 Vue Router Vue.js $router.push 便
this.$router.push('/ums/addMenu');
},
getList() {
//
this.listLoading = true;
// API ID ID Promise then
fetchList(this.parentId, this.listQuery).then(response => {
//
this.listLoading = false;
// list :data="list"
this.list = response.data.list;
// total
this.total = response.data.total;
});
},
// handleSizeChange
handleSizeChange(val) {
//
//
//
// listQuerypageNum 1
//
//
this.listQuery.pageNum = 1;
// listQuerypageSizeval
// val
// getList
// 1
//
this.getList(); this.getList();
}, },
watch: {
$route(route) { // handleCurrentChange
this.resetParentId(); handleCurrentChange(val) {
this.getList(); //
} // val
// listQuerypageNumval
// 便
// getList
// 使
this.listQuery.pageNum = val;
this.getList();
}, },
methods: {
resetParentId(){ // handleHiddenChange
this.listQuery.pageNum = 1; handleHiddenChange(index, row) {
if (this.$route.query.parentId != null) { //
this.parentId = this.$route.query.parentId; // el-switch
} else { // index
this.parentId = 0; // row
}
}, // updateHidden API
handleAddMenu() { // ID row.id {hidden: row.hidden} hidden
this.$router.push('/ums/addMenu');
}, // this.$message
getList() { // success绿
this.listLoading = true; // 1000 1 使
fetchList(this.parentId, this.listQuery).then(response => { updateHidden(row.id, {hidden: row.hidden}).then(response => {
this.listLoading = false; this.$message({
this.list = response.data.list; message: '修改成功',
this.total = response.data.total; type: 'success',
duration: 1000
}); });
}, });
handleSizeChange(val) { },
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val; // handleShowNextLevel
this.getList(); handleShowNextLevel(index, row) {
}, //
handleCurrentChange(val) { //
this.listQuery.pageNum = val;
this.getList(); // 使 this.$router.push Vue Router
}, // /ums/menu ID parentId query
handleHiddenChange(index, row) { // /ums/menu
updateHidden(row.id,{hidden:row.hidden}).then(response=>{ // 便
this.$router.push({path: '/ums/menu', query: {parentId: row.id}});
},
// handleUpdate
handleUpdate(index, row) {
//
//
// this.$router.push /ums/updateMenu ID id query
// /ums/updateMenu
// 便便
this.$router.push({path: '/ums/updateMenu', query: {id: row.id}});
},
// handleDelete
handleDelete(index, row) {
//
//
//
//
// warning
// then
// deleteMenu API ID row.id
// this.$message
// success 1000 1 便
// getList 使
this.$confirm('是否要删除该菜单', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteMenu(row.id).then(response => {
this.$message({ this.$message({
message: '修改成功', message: '删除成功',
type: 'success', type: 'success',
duration: 1000 duration: 1000
}); });
this.getList();
}); });
}, });
handleShowNextLevel(index, row) {
this.$router.push({path: '/ums/menu', query: {parentId: row.id}})
},
handleUpdate(index, row) {
this.$router.push({path:'/ums/updateMenu',query:{id:row.id}});
},
handleDelete(index, row) {
this.$confirm('是否要删除该菜单', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteMenu(row.id).then(response => {
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
this.getList();
});
});
}
},
filters: {
levelFilter(value) {
if (value === 0) {
return '一级';
} else if (value === 1) {
return '二级';
}
},
disableNextLevel(value) {
if (value === 0) {
return false;
} else {
return true;
}
}
} }
} }
}
</script> </script>
<style scoped> <style scoped>
/*
scoped 属性表示样式仅作用于当前组件内的元素避免样式冲突 */
/* CSS resourceList
- 设置表格的样式如表头单元格的字体颜色边框样式等
- 对筛选区域操作按钮分页组件等元素的布局外观进行调整比如按钮的颜色大小间距等
- 定制对话框的样式像对话框的背景色边框按钮在对话框内的排版等
通过添加这些样式规则可以让页面的显示更加符合设计要求和美观性 */
</style> </style>

@ -1,14 +1,28 @@
<template>  <template>
<!-- 使用 MenuDetail 组件并通过绑定 :is-edit 属性为 true MenuDetail 组件传递当前处于编辑状态的信息
MenuDetail 组件大概率是一个自定义组件用于展示和处理菜单详情相关的表单等内容在这里用于编辑菜单的场景 -->
<menu-detail :is-edit='true'></menu-detail> <menu-detail :is-edit='true'></menu-detail>
</template> </template>
<script> <script>
import MenuDetail from './components/MenuDetail' // ./components/ MenuDetail MenuDetail components
export default { // 使
name: 'updateMenu', import MenuDetail from './components/MenuDetail';
components: { MenuDetail }
} export default {
name: 'updateMenu',
// components MenuDetail 使template使
// Vue.js MenuDetail
components: { MenuDetail }
}
</script> </script>
<style> <style>
/* CSS updateMenu
来对组件的外观进行定制化显示 */
/* CSS resourceList
- 设置表格的样式如表头单元格的字体颜色边框样式等
- 对筛选区域操作按钮分页组件等元素的布局外观进行调整比如按钮的颜色大小间距等
- 定制对话框的样式像对话框的背景色边框按钮在对话框内的排版等
通过添加这些样式规则可以让页面的显示更加符合设计要求和美观性 */
</style> </style>

@ -1,27 +1,39 @@
<template>  <template>
<!-- 整体的应用容器 div -->
<div class="app-container"> <div class="app-container">
<!-- 操作区域的卡片容器设置了阴影效果为无 -->
<el-card shadow="never" class="operate-container"> <el-card shadow="never" class="operate-container">
<!-- 图标元素使用了 el-icon-tickets 图标 -->
<i class="el-icon-tickets"></i> <i class="el-icon-tickets"></i>
<!-- 文字提示显示数据列表字样 -->
<span>数据列表</span> <span>数据列表</span>
<!-- 添加按钮设置了按钮大小为 mini添加了自定义类名 btn-add点击时调用 handleAdd 方法 -->
<el-button size="mini" class="btn-add" @click="handleAdd()"></el-button> <el-button size="mini" class="btn-add" @click="handleAdd()"></el-button>
</el-card> </el-card>
<!-- 表格容器 div用于放置展示数据的表格 -->
<div class="table-container"> <div class="table-container">
<!-- el-table 组件用于展示数据列表设置了引用名绑定的数据表格宽度加载状态绑定以及边框等属性 -->
<el-table ref="resourceCategoryTable" <el-table ref="resourceCategoryTable"
:data="list" :data="list"
style="width: 100%;" style="width: 100%;"
v-loading="listLoading" border> v-loading="listLoading" border>
<!-- 表格列标签为编号设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 id 属性 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为名称内容居中对齐通过插槽作用域展示对应行数据的 name 属性 -->
<el-table-column label="名称" align="center"> <el-table-column label="名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为创建时间内容居中对齐通过插槽作用域展示对应行数据的 createTime 属性并使用 formatDateTime 过滤器对时间进行格式化展示 -->
<el-table-column label="创建时间" align="center"> <el-table-column label="创建时间" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template> <template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为排序内容居中对齐通过插槽作用域展示对应行数据的 sort 属性 -->
<el-table-column label="排序" align="center"> <el-table-column label="排序" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template> <template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为操作设置了宽度和内容居中对齐通过插槽作用域展示编辑和删除按钮分别点击时调用 handleUpdate handleDelete 方法 -->
<el-table-column label="操作" width="180" align="center"> <el-table-column label="操作" width="180" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button size="mini" <el-button size="mini"
@ -36,20 +48,25 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- el-dialog 对话框组件设置了标题为添加分类绑定了显示状态设置了宽度 -->
<el-dialog <el-dialog
title="添加分类" title="添加分类"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
width="40%"> width="40%">
<!-- 对话框内的表单绑定了 resourceCategory 数据对象设置了表单引用标签宽度和大小 -->
<el-form :model="resourceCategory" <el-form :model="resourceCategory"
ref="resourceCategoryForm" ref="resourceCategoryForm"
label-width="150px" size="small"> label-width="150px" size="small">
<!-- 表单项目标签为名称输入框双向绑定 resourceCategory.name并设置了宽度 -->
<el-form-item label="名称:"> <el-form-item label="名称:">
<el-input v-model="resourceCategory.name" style="width: 250px"></el-input> <el-input v-model="resourceCategory.name" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为排序输入框双向绑定 resourceCategory.sort并设置了宽度 -->
<el-form-item label="排序:"> <el-form-item label="排序:">
<el-input v-model="resourceCategory.sort" style="width: 250px"></el-input> <el-input v-model="resourceCategory.sort" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 对话框底部的按钮区域通过插槽定义了取消和确定按钮分别绑定了对应的点击事件 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button> <el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button> <el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
@ -58,99 +75,133 @@
</div> </div>
</template> </template>
<script> <script>
import {listAllCate,createResourceCategory,updateResourceCategory,deleteResourceCategory} from '@/api/resourceCategory'; // @/api/resourceCategory API
import {formatDate} from '@/utils/date'; import {listAllCate,createResourceCategory,updateResourceCategory,deleteResourceCategory} from '@/api/resourceCategory';
const defaultResourceCategory={ // @/utils/date
name:null, import {formatDate} from '@/utils/date';
sort:0
}; // null 0
export default { const defaultResourceCategory={
name: 'resourceCategoryList', name:null,
data() { sort:0
return { };
list: null,
listLoading: false, export default {
dialogVisible:false, name: 'resourceCategoryList',
isEdit:false, data() {
resourceCategory:Object.assign({},defaultResourceCategory) return {
// null API
list: null,
// false
listLoading: false,
// / false
dialogVisible:false,
// false
isEdit:false,
// defaultResourceCategory
resourceCategory:Object.assign({},defaultResourceCategory)
}
},
created() {
//
this.getList();
},
filters: {
formatDateTime(time) {
// 'N/A'
if (time == null || time === '') {
return 'N/A';
} }
// Date 便
let date = new Date(time);
// 使 formatDate
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
},
methods: {
handleAdd() {
// /isEdit false resourceCategory
this.dialogVisible = true;
this.isEdit = false;
this.resourceCategory = Object.assign({},defaultResourceCategory);
}, },
created() { handleUpdate(index,row){
this.getList(); // /isEdit true resourceCategory
this.dialogVisible = true;
this.isEdit = true;
this.resourceCategory = Object.assign({},row);
}, },
filters:{ handleDelete(index,row){
formatDateTime(time) { //
if (time == null || time === '') { this.$confirm('是否要删除该分类?', '提示', {
return 'N/A'; confirmButtonText: '确定',
} cancelButtonText: '取消',
let date = new Date(time); type: 'warning'
return formatDate(date, 'yyyy-MM-dd hh:mm:ss') }).then(() => {
} // API id
//
deleteResourceCategory(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
}, },
methods: { handleDialogConfirm() {
handleAdd() { // /
this.dialogVisible = true; this.$confirm('是否要确认?', '提示', {
this.isEdit = false; confirmButtonText: '确定',
this.resourceCategory = Object.assign({},defaultResourceCategory); cancelButtonText: '取消',
}, type: 'warning'
handleUpdate(index,row){ }).then(() => {
this.dialogVisible = true; // isEdit true
this.isEdit = true; if (this.isEdit) {
this.resourceCategory = Object.assign({},row); // API id resourceCategory
}, //
handleDelete(index,row){ updateResourceCategory(this.resourceCategory.id,this.resourceCategory).then(response => {
this.$confirm('是否要删除该分类?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteResourceCategory(row.id).then(response => {
this.$message({ this.$message({
type: 'success', message: '修改成功!',
message: '删除成功!' type: 'success'
}); });
this.dialogVisible =false;
this.getList(); this.getList();
}); })
}); } else {
}, // API resourceCategory
handleDialogConfirm() { //
this.$confirm('是否要确认?', '提示', { createResourceCategory(this.resourceCategory).then(response => {
confirmButtonText: '确定', this.$message({
cancelButtonText: '取消', message: '添加成功!',
type: 'warning' type: 'success'
}).then(() => { });
if (this.isEdit) { this.dialogVisible =false;
updateResourceCategory(this.resourceCategory.id,this.resourceCategory).then(response => { this.getList();
this.$message({ })
message: '修改成功!', }
type: 'success' })
}); },
this.dialogVisible =false; getList() {
this.getList(); //
}) this.listLoading = true;
} else { // API API
createResourceCategory(this.resourceCategory).then(response => { listAllCate({}).then(response => {
this.$message({ //
message: '添加成功!', this.listLoading = false;
type: 'success' // list
}); this.list = response.data;
this.dialogVisible =false; });
this.getList();
})
}
})
},
getList() {
this.listLoading = true;
listAllCate({}).then(response => {
this.listLoading = false;
this.list = response.data;
});
}
} }
} }
}
</script> </script>
<style> <style>
/* CSS resourceCategoryList
例如设置表格样式对话框样式按钮样式以及整体布局相关的样式等来美化组件的外观显示效果 */
/* CSS resourceList
- 设置表格的样式如表头单元格的字体颜色边框样式等
- 对筛选区域操作按钮分页组件等元素的布局外观进行调整比如按钮的颜色大小间距等
- 定制对话框的样式像对话框的背景色边框按钮在对话框内的排版等
通过添加这些样式规则可以让页面的显示更加符合设计要求和美观性 */
</style> </style>

@ -1,9 +1,14 @@
<template>  <template>
<!-- 整体的应用容器 div用于包裹页面内的各个功能模块 -->
<div class="app-container"> <div class="app-container">
<!-- 筛选搜索区域的卡片容器设置了阴影效果为无 -->
<el-card class="filter-container" shadow="never"> <el-card class="filter-container" shadow="never">
<div> <div>
<!-- 搜索图标元素使用了 el-icon-search 图标 -->
<i class="el-icon-search"></i> <i class="el-icon-search"></i>
<!-- 筛选搜索的文字提示 -->
<span>筛选搜索</span> <span>筛选搜索</span>
<!-- 查询搜索按钮设置了样式为右浮动按钮类型为主要primary点击时调用 handleSearchList 方法按钮大小为 small -->
<el-button <el-button
style="float:right" style="float:right"
type="primary" type="primary"
@ -11,6 +16,7 @@
size="small"> size="small">
查询搜索 查询搜索
</el-button> </el-button>
<!-- 重置按钮设置了样式为右浮动且距离右侧 15px点击时调用 handleResetSearch 方法按钮大小为 small -->
<el-button <el-button
style="float:right;margin-right: 15px" style="float:right;margin-right: 15px"
@click="handleResetSearch()" @click="handleResetSearch()"
@ -19,13 +25,17 @@
</el-button> </el-button>
</div> </div>
<div style="margin-top: 15px"> <div style="margin-top: 15px">
<!-- 内联表单绑定了 listQuery 数据对象表单大小为 small标签宽度为 140px用于设置筛选条件 -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px"> <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单项目标签为资源名称内部的输入框双向绑定 listQuery.nameKeyword设置了类名占位符以及可清空功能 -->
<el-form-item label="资源名称:"> <el-form-item label="资源名称:">
<el-input v-model="listQuery.nameKeyword" class="input-width" placeholder="资源名称" clearable></el-input> <el-input v-model="listQuery.nameKeyword" class="input-width" placeholder="资源名称" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为资源路径内部的输入框双向绑定 listQuery.urlKeyword设置了类名占位符以及可清空功能 -->
<el-form-item label="资源路径:"> <el-form-item label="资源路径:">
<el-input v-model="listQuery.urlKeyword" class="input-width" placeholder="资源路径" clearable></el-input> <el-input v-model="listQuery.urlKeyword" class="input-width" placeholder="资源路径" clearable></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为资源分类内部是一个下拉选择框双向绑定 listQuery.categoryId设置了占位符可清空以及类名通过循环渲染选项 -->
<el-form-item label="资源分类:"> <el-form-item label="资源分类:">
<el-select v-model="listQuery.categoryId" placeholder="全部" clearable class="input-width"> <el-select v-model="listQuery.categoryId" placeholder="全部" clearable class="input-width">
<el-option v-for="item in categoryOptions" <el-option v-for="item in categoryOptions"
@ -38,32 +48,45 @@
</el-form> </el-form>
</div> </div>
</el-card> </el-card>
<!-- 操作区域的卡片容器设置了阴影效果为无 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<!-- 图标元素使用了 el-icon-tickets 图标 -->
<i class="el-icon-tickets"></i> <i class="el-icon-tickets"></i>
<!-- 数据列表的文字提示 -->
<span>数据列表</span> <span>数据列表</span>
<!-- 添加按钮大小为 mini添加了自定义类名 btn-add点击时调用 handleAdd 方法设置了左侧外边距为 20px -->
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button> <el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
<!-- 资源分类按钮大小为 mini添加了自定义类名 btn-add点击时调用 handleShowCategory 方法用于跳转到资源分类页面 -->
<el-button size="mini" class="btn-add" @click="handleShowCategory()"></el-button> <el-button size="mini" class="btn-add" @click="handleShowCategory()"></el-button>
</el-card> </el-card>
<!-- 表格容器 div用于放置展示资源数据的表格 -->
<div class="table-container"> <div class="table-container">
<!-- el-table 组件用于展示资源数据列表设置了引用名绑定的数据表格宽度加载状态绑定以及边框等属性 -->
<el-table ref="resourceTable" <el-table ref="resourceTable"
:data="list" :data="list"
style="width: 100%;" style="width: 100%;"
v-loading="listLoading" border> v-loading="listLoading" border>
<!-- 表格列标签为编号设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 id 属性 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为资源名称内容居中对齐通过插槽作用域展示对应行数据的 name 属性 -->
<el-table-column label="资源名称" align="center"> <el-table-column label="资源名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为资源路径内容居中对齐通过插槽作用域展示对应行数据的 url 属性 -->
<el-table-column label="资源路径" align="center"> <el-table-column label="资源路径" align="center">
<template slot-scope="scope">{{scope.row.url}}</template> <template slot-scope="scope">{{scope.row.url}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为描述内容居中对齐通过插槽作用域展示对应行数据的 description 属性 -->
<el-table-column label="描述" align="center"> <el-table-column label="描述" align="center">
<template slot-scope="scope">{{scope.row.description}}</template> <template slot-scope="scope">{{scope.row.description}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为添加时间设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 createTime 属性并使用 formatDateTime 过滤器对时间进行格式化展示 -->
<el-table-column label="添加时间" width="160" align="center"> <el-table-column label="添加时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template> <template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为操作设置了宽度和内容居中对齐通过插槽作用域展示编辑和删除按钮分别点击时调用 handleUpdate handleDelete 方法 -->
<el-table-column label="操作" width="140" align="center"> <el-table-column label="操作" width="140" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button size="mini" <el-button size="mini"
@ -79,7 +102,9 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 分页容器 div用于放置分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<!-- el-pagination 分页组件设置了背景色相关页面切换事件绑定布局样式当前页每页数量可选每页数量数组以及总记录数等属性 -->
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@ -91,19 +116,24 @@
:total="total"> :total="total">
</el-pagination> </el-pagination>
</div> </div>
<!-- el-dialog 对话框组件根据 isEdit 变量动态设置标题为编辑资源添加资源绑定了显示状态设置了宽度 -->
<el-dialog <el-dialog
:title="isEdit?'编辑资源':'添加资源'" :title="isEdit?'编辑资源':'添加资源'"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
width="40%"> width="40%">
<!-- 对话框内的表单绑定了 resource 数据对象设置了表单引用标签宽度和大小 -->
<el-form :model="resource" <el-form :model="resource"
ref="resourceForm" ref="resourceForm"
label-width="150px" size="small"> label-width="150px" size="small">
<!-- 表单项目标签为资源名称输入框双向绑定 resource.name并设置了宽度 -->
<el-form-item label="资源名称:"> <el-form-item label="资源名称:">
<el-input v-model="resource.name" style="width: 250px"></el-input> <el-input v-model="resource.name" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为资源路径输入框双向绑定 resource.url并设置了宽度 -->
<el-form-item label="资源路径:"> <el-form-item label="资源路径:">
<el-input v-model="resource.url" style="width: 250px"></el-input> <el-input v-model="resource.url" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为资源分类内部是一个下拉选择框双向绑定 resource.categoryId设置了占位符可清空以及宽度通过循环渲染选项 -->
<el-form-item label="资源分类:"> <el-form-item label="资源分类:">
<el-select v-model="resource.categoryId" placeholder="全部" clearable style="width: 250px"> <el-select v-model="resource.categoryId" placeholder="全部" clearable style="width: 250px">
<el-option v-for="item in categoryOptions" <el-option v-for="item in categoryOptions"
@ -113,6 +143,7 @@
</el-option> </el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 表单项目标签为描述输入框双向绑定 resource.description设置了文本域类型和行数以及宽度 -->
<el-form-item label="描述:"> <el-form-item label="描述:">
<el-input v-model="resource.description" <el-input v-model="resource.description"
type="textarea" type="textarea"
@ -120,6 +151,7 @@
style="width: 250px"></el-input> style="width: 250px"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 对话框底部的按钮区域通过插槽定义了取消和确定按钮分别绑定了对应的点击事件 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button> <el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button> <el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
@ -128,146 +160,205 @@
</div> </div>
</template> </template>
<script> <script>
import {fetchList,createResource,updateResource,deleteResource} from '@/api/resource'; // @/api/resource API
import {listAllCate} from '@/api/resourceCategory'; import {fetchList,createResource,updateResource,deleteResource} from '@/api/resource';
import {formatDate} from '@/utils/date'; // @/api/resourceCategory API
import {listAllCate} from '@/api/resourceCategory';
// @/utils/date
import {formatDate} from '@/utils/date';
const defaultListQuery = { // ID
pageNum: 1, const defaultListQuery = {
pageSize: 10, pageNum: 1,
nameKeyword: null, pageSize: 10,
urlKeyword: null, nameKeyword: null,
categoryId:null urlKeyword: null,
}; categoryId:null
const defaultResource = { };
id: null, // id ID
name: null, const defaultResource = {
url: null, id: null,
categoryId: null, name: null,
description:'' url: null,
}; categoryId: null,
export default { description:''
name: 'resourceList', };
data() {
return { export default {
listQuery: Object.assign({}, defaultListQuery), name: 'resourceList',
list: null, data() {
total: null, return {
listLoading: false, // defaultListQuery
dialogVisible: false, listQuery: Object.assign({}, defaultListQuery),
resource: Object.assign({}, defaultResource), // null API
isEdit: false, list: null,
categoryOptions:[], // null
defaultCategoryId:null total: null,
// false
listLoading: false,
// / false
dialogVisible: false,
// defaultResource
resource: Object.assign({}, defaultResource),
// false
isEdit: false,
// label value
categoryOptions: [],
// ID null
defaultCategoryId: null
}
},
created() {
//
this.getList();
this.getCateList();
},
filters: {
formatDateTime(time) {
// 'N/A'
if (time == null || time === '') {
return 'N/A';
} }
// Date 便
let date = new Date(time);
// 使 formatDate
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
},
methods: {
handleResetSearch() {
//
this.listQuery = Object.assign({}, defaultListQuery);
}, },
created() { handleSearchList() {
// 1
this.listQuery.pageNum = 1;
this.getList(); this.getList();
this.getCateList();
}, },
filters: { handleSizeChange(val) {
formatDateTime(time) { // 1
if (time == null || time === '') { this.listQuery.pageNum = 1;
return 'N/A'; this.listQuery.pageSize = val;
} this.getList();
let date = new Date(time); },
return formatDate(date, 'yyyy-MM-dd hh:mm:ss') handleCurrentChange(val) {
} //
this.listQuery.pageNum = val;
this.getList();
}, },
methods: { handleAdd() {
handleResetSearch() { // /isEdit false resource ID
this.listQuery = Object.assign({}, defaultListQuery); this.dialogVisible = true;
}, this.isEdit = false;
handleSearchList() { this.resource = Object.assign({}, defaultResource);
this.listQuery.pageNum = 1; this.resource.categoryId = this.defaultCategoryId;
this.getList(); },
}, handleDelete(index, row) {
handleSizeChange(val) { //
this.listQuery.pageNum = 1; this.$confirm('是否要删除该资源?', '提示', {
this.listQuery.pageSize = val; confirmButtonText: '确定',
this.getList(); cancelButtonText: '取消',
}, type: 'warning'
handleCurrentChange(val) { }).then(() => {
this.listQuery.pageNum = val; // API id
this.getList(); //
}, deleteResource(row.id).then(response => {
handleAdd() { this.$message({
this.dialogVisible = true; type: 'success',
this.isEdit = false; message: '删除成功!'
this.resource = Object.assign({},defaultResource); });
this.resource.categoryId = this.defaultCategoryId; this.getList();
}, });
handleDelete(index, row) { });
this.$confirm('是否要删除该资源?', '提示', { },
confirmButtonText: '确定', handleUpdate(index, row) {
cancelButtonText: '取消', // /isEdit true resource
type: 'warning' this.dialogVisible = true;
}).then(() => { this.isEdit = true;
deleteResource(row.id).then(response => { this.resource = Object.assign({}, row);
},
handleDialogConfirm() {
// /
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// isEdit true
if (this.isEdit) {
// API id resource
//
updateResource(this.resource.id, this.resource).then(response => {
this.$message({ this.$message({
type: 'success', message: '修改成功!',
message: '删除成功!' type: 'success'
}); });
this.dialogVisible = false;
this.getList(); this.getList();
}); })
}); } else {
}, // API resource
handleUpdate(index, row) { //
this.dialogVisible = true; createResource(this.resource).then(response => {
this.isEdit = true; this.$message({
this.resource = Object.assign({},row); message: '添加成功!',
}, type: 'success'
handleDialogConfirm() { });
this.$confirm('是否要确认?', '提示', { this.dialogVisible = false;
confirmButtonText: '确定', this.getList();
cancelButtonText: '取消', })
type: 'warning' }
}).then(() => { })
if (this.isEdit) { },
updateResource(this.resource.id,this.resource).then(response => { //
this.$message({ // '/ums/resourceCategory'
message: '修改成功!', //
type: 'success' handleShowCategory() {
}); this.$router.push({path: '/ums/resourceCategory'})
this.dialogVisible =false; },
this.getList();
}) //
} else { // listLoading true
createResource(this.resource).then(response => { //
this.$message({ // fetchList API listQuery
message: '添加成功!', // then listLoading false
type: 'success' // response list list
}); // total total
this.dialogVisible =false; getList() {
this.getList(); this.listLoading = true;
}) fetchList(this.listQuery).then(response => {
} this.listLoading = false;
}) this.list = response.data.list;
}, this.total = response.data.total;
handleShowCategory(){ });
this.$router.push({path: '/ums/resourceCategory'}) },
},
getList() { //
this.listLoading = true; // listAllCate API Promise then
fetchList(this.listQuery).then(response => { getCateList() {
this.listLoading = false; listAllCate().then(response => {
this.list = response.data.list; // cateList 便
this.total = response.data.total; let cateList = response.data;
}); //
}, for (let i = 0; i < cateList.length; i++) {
getCateList(){ // cate 便
listAllCate().then(response=>{ let cate = cateList[i];
let cateList = response.data; // label ID value categoryOptions
for(let i=0;i<cateList.length;i++){ // categoryOptions UI 便
let cate = cateList[i]; this.categoryOptions.push({label: cate.name, value: cate.id});
this.categoryOptions.push({label:cate.name,value:cate.id}); }
} // ID defaultCategoryId
this.defaultCategoryId=cateList[0].id; // ID 使使
}) this.defaultCategoryId = cateList[0].id;
} })
} }
} }
}
</script> </script>
<style></style> <style>
/* CSS resourceList
- 设置表格的样式如表头单元格的字体颜色边框样式等
- 对筛选区域操作按钮分页组件等元素的布局外观进行调整比如按钮的颜色大小间距等
- 定制对话框的样式像对话框的背景色边框按钮在对话框内的排版等
通过添加这些样式规则可以让页面的显示更加符合设计要求和美观性 */
</style>

@ -1,5 +1,8 @@
<template> <template>
<!-- 使用 el-card 组件创建一个表单容器卡片设置阴影效果为无 -->
<el-card class="form-container" shadow="never"> <el-card class="form-container" shadow="never">
<!-- el-tree 组件用于展示树形结构的数据绑定了 menuTreeList 数据作为树的数据源 -->
<!-- 显示复选框默认展开所有节点设置节点的唯一标识键为 'id'添加了引用名 'tree'高亮当前选中节点并配置了节点属性映射 -->
<el-tree <el-tree
:data="menuTreeList" :data="menuTreeList"
show-checkbox show-checkbox
@ -9,93 +12,131 @@
highlight-current highlight-current
:props="defaultProps"> :props="defaultProps">
</el-tree> </el-tree>
<!-- 包含两个按钮的 div 容器设置了顶部外边距为 20px并使其内容居中对齐 -->
<div style="margin-top: 20px" align="center"> <div style="margin-top: 20px" align="center">
<!-- 保存按钮类型为主要primary点击时调用 handleSave 方法 -->
<el-button type="primary" @click="handleSave()"></el-button> <el-button type="primary" @click="handleSave()"></el-button>
<!-- 清空按钮点击时调用 handleClear 方法 -->
<el-button @click="handleClear()"></el-button> <el-button @click="handleClear()"></el-button>
</div> </div>
</el-card> </el-card>
</template> </template>
<script> <script>
import {fetchTreeList} from '@/api/menu'; // @/api/menu API
import {listMenuByRole,allocMenu} from '@/api/role'; import {fetchTreeList} from '@/api/menu';
// @/api/role API
import {listMenuByRole, allocMenu} from '@/api/role';
export default { export default {
name: "allocMenu", name: "allocMenu",
data() { data() {
return { return {
menuTreeList: [], // API
defaultProps: { menuTreeList: [],
children: 'children', // el-tree 'children' 'title'
label: 'title' defaultProps: {
}, children: 'children',
roleId:null label: 'title'
};
},
created() {
this.roleId = this.$route.query.roleId;
this.treeList();
this.getRoleMenu(this.roleId);
},
methods: {
treeList() {
fetchTreeList().then(response => {
this.menuTreeList = response.data;
});
}, },
getRoleMenu(roleId){ // ID null
listMenuByRole(roleId).then(response=>{ roleId: null
let menuList = response.data; };
let checkedMenuIds=[]; },
if(menuList!=null&&menuList.length>0){ created() {
for(let i=0;i<menuList.length;i++){ // ID roleId
let menu = menuList[i]; this.roleId = this.$route.query.roleId;
if(menu.parentId!==0){ //
checkedMenuIds.push(menu.id); this.treeList();
} // ID
this.getRoleMenu(this.roleId);
},
methods: {
treeList() {
// API Promise menuTreeList el-tree
fetchTreeList().then(response => {
this.menuTreeList = response.data;
});
},
getRoleMenu(roleId) {
// API ID Promise
listMenuByRole(roleId).then(response => {
// menuList 便
let menuList = response.data;
// ID
let checkedMenuIds = [];
//
if (menuList!= null && menuList.length > 0) {
for (let i = 0; i < menuList.length; i++) {
//
let menu = menuList[i];
// ID 0 ID ID
if (menu.parentId!== 0) {
checkedMenuIds.push(menu.id);
} }
} }
this.$refs.tree.setCheckedKeys(checkedMenuIds); }
}); // el-tree ref='tree' ID
}, this.$refs.tree.setCheckedKeys(checkedMenuIds);
handleSave() { });
let checkedNodes = this.$refs.tree.getCheckedNodes(); },
let checkedMenuIds=new Set(); handleSave() {
if(checkedNodes!=null&&checkedNodes.length>0){ // el-tree
for(let i=0;i<checkedNodes.length;i++){ let checkedNodes = this.$refs.tree.getCheckedNodes();
let checkedNode = checkedNodes[i]; // 使 Set ID ID
checkedMenuIds.add(checkedNode.id); let checkedMenuIds = new Set();
if(checkedNode.parentId!==0){ //
checkedMenuIds.add(checkedNode.parentId); if (checkedNodes!= null && checkedNodes.length > 0) {
} for (let i = 0; i < checkedNodes.length; i++) {
//
let checkedNode = checkedNodes[i];
// ID Set
checkedMenuIds.add(checkedNode.id);
// ID 0 ID Set
if (checkedNode.parentId!== 0) {
checkedMenuIds.add(checkedNode.parentId);
} }
} }
this.$confirm('是否分配菜单?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(()=>{
let params = new URLSearchParams();
params.append("roleId",this.roleId);
params.append("menuIds",Array.from(checkedMenuIds));
allocMenu(params).then(response => {
this.$message({
message: '分配成功',
type: 'success',
duration: 1000
});
this.$router.back();
})
})
},
handleClear() {
this.$refs.tree.setCheckedKeys([]);
} }
//
this.$confirm('是否分配菜单?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// URLSearchParams
let params = new URLSearchParams();
// ID
params.append("roleId", this.roleId);
// ID Array.from
params.append("menuIds", Array.from(checkedMenuIds));
// API Promise
allocMenu(params).then(response => {
// success 1000
this.$message({
message: '分配成功',
type: 'success',
duration: 1000
});
//
this.$router.back();
})
})
},
handleClear() {
// el-tree ID
this.$refs.tree.setCheckedKeys([]);
} }
} }
}
</script> </script>
<style scoped> <style scoped>
/* CSS allocMenu
例如设置 el-tree 组件的样式像节点的字体颜色间距等以及按钮的样式包括按钮的大小颜色边框等
scoped 属性表示样式仅作用于当前组件内的元素避免样式冲突 */
/* CSS resourceList
- 设置表格的样式如表头单元格的字体颜色边框样式等
- 对筛选区域操作按钮分页组件等元素的布局外观进行调整比如按钮的颜色大小间距等
- 定制对话框的样式像对话框的背景色边框按钮在对话框内的排版等
通过添加这些样式规则可以让页面的显示更加符合设计要求和美观性 */
</style> </style>

@ -1,187 +1,303 @@
<template> <template>
<!-- 使用 el-card 组件创建一个表单容器卡片设置阴影效果为无 -->
<el-card class="form-container" shadow="never"> <el-card class="form-container" shadow="never">
<!-- 使用 v-for 指令循环遍历 allResourceCate 数组中的每个元素cate同时传递索引index -->
<!-- 根据索引判断是否添加 'top-line' 类名第一个元素添加用于样式区分可能是添加顶部边框之类的效果并设置了唯一的 key -->
<div v-for="(cate,index) in allResourceCate" :class="index===0?'top-line':null" :key="'cate'+cate.id"> <div v-for="(cate,index) in allResourceCate" :class="index===0?'top-line':null" :key="'cate'+cate.id">
<!-- el-row 组件添加了 'table-layout' 类名用于样式布局设置了背景颜色 -->
<el-row class="table-layout" style="background: #F2F6FC;"> <el-row class="table-layout" style="background: #F2F6FC;">
<!-- el-checkbox 复选框组件双向绑定 cate 对象的 checked 属性通过调用 isIndeterminate 方法设置不确定状态绑定 change 事件到 handleCheckAllChange 方法 -->
<!-- 复选框显示的文本为 cate 对象的 name 属性值 -->
<el-checkbox v-model="cate.checked" <el-checkbox v-model="cate.checked"
:indeterminate="isIndeterminate(cate.id)" :indeterminate="isIndeterminate(cate.id)"
@change="handleCheckAllChange(cate)"> @change="handleCheckAllChange(cate)">
{{cate.name}} {{cate.name}}
</el-checkbox> </el-checkbox>
</el-row> </el-row>
<!-- 另一个 el-row 组件同样添加了 'table-layout' 类名用于样式布局 -->
<el-row class="table-layout"> <el-row class="table-layout">
<!-- 使用 v-for 指令循环遍历当前分类cate下的资源列表通过 getResourceByCate 方法获取每个资源对应一个 el-checkbox 组件 -->
<!-- 设置了 el-col 组件的占比为 8给每个复选框添加了上下内边距为 4px 的样式 -->
<el-col :span="8" v-for="resource in getResourceByCate(cate.id)" :key="resource.id" style="padding: 4px 0"> <el-col :span="8" v-for="resource in getResourceByCate(cate.id)" :key="resource.id" style="padding: 4px 0">
<!-- el-checkbox 复选框组件双向绑定 resource 对象的 checked 属性绑定 change 事件到 handleCheckChange 方法 -->
<!-- 复选框显示的文本为 resource 对象的 name 属性值 -->
<el-checkbox v-model="resource.checked" @change="handleCheckChange(resource)"> <el-checkbox v-model="resource.checked" @change="handleCheckChange(resource)">
{{resource.name}} {{resource.name}}
</el-checkbox> </el-checkbox>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<!-- 包含两个按钮的 div 容器设置了顶部外边距为 20px并使其内容居中对齐 -->
<div style="margin-top: 20px" align="center"> <div style="margin-top: 20px" align="center">
<!-- 保存按钮类型为主要primary点击时调用 handleSave 方法 -->
<el-button type="primary" @click="handleSave()"></el-button> <el-button type="primary" @click="handleSave()"></el-button>
<!-- 清空按钮点击时调用 handleClear 方法 -->
<el-button @click="handleClear()"></el-button> <el-button @click="handleClear()"></el-button>
</div> </div>
</el-card> </el-card>
</template> </template>
<script> <script>
import {fetchAllResourceList} from '@/api/resource'; // @/api/resource API
import {listAllCate} from '@/api/resourceCategory'; import {fetchAllResourceList} from '@/api/resource';
import {allocResource,listResourceByRole} from '@/api/role'; // @/api/resourceCategory API
import {listAllCate} from '@/api/resourceCategory';
// @/api/role API
import {allocResource, listResourceByRole} from '@/api/role';
export default { export default {
name: "allocResource", name: "allocResource",
data() { data() {
return { return {
roleId: null, // ID null
allResource: null, roleId: null,
allResourceCate: null // null API
}; allResource: null,
}, // null API
created() { allResourceCate: null
this.roleId = this.$route.query.roleId; };
this.getAllResourceCateList(); },
}, created() {
methods: { // ID roleId
getAllResourceList() { this.roleId = this.$route.query.roleId;
fetchAllResourceList().then(response => { //
this.allResource = response.data; this.getAllResourceCateList();
for (let i = 0; i < this.allResource.length; i++) { },
this.allResource[i].checked = false; methods: {
} //
this.getResourceByRole(this.roleId); getAllResourceList() {
}); // API Promise
}, fetchAllResourceList().then(response => {
getAllResourceCateList() { // allResource
listAllCate().then(response => { this.allResource = response.data;
this.allResourceCate = response.data; // checked false
for (let i = 0; i < this.allResourceCate.length; i++) {
this.allResourceCate[i].checked = false;
}
this.getAllResourceList();
});
},
getResourceByCate(categoryId) {
let cateResource = [];
if (this.allResource == null) return null;
for (let i = 0; i < this.allResource.length; i++) { for (let i = 0; i < this.allResource.length; i++) {
let resource = this.allResource[i]; this.allResource[i].checked = false;
if (resource.categoryId === categoryId) {
cateResource.push(resource);
}
}
return cateResource;
},
getResourceByRole(roleId){
listResourceByRole(roleId).then(response=>{
let allocResource = response.data;
this.allResource.forEach(item=>{
item.checked = this.getResourceChecked(item.id,allocResource);
});
this.allResourceCate.forEach(item=>{
item.checked = this.isAllChecked(item.id);
});
this.$forceUpdate();
});
},
getResourceChecked(resourceId,allocResource){
if(allocResource==null||allocResource.length===0) return false;
for(let i=0;i<allocResource.length;i++){
if(allocResource[i].id===resourceId){
return true;
}
}
return false;
},
isIndeterminate(categoryId) {
let cateResources = this.getResourceByCate(categoryId);
if (cateResources == null) return false;
let checkedCount = 0;
for (let i = 0; i < cateResources.length; i++) {
if (cateResources[i].checked === true) {
checkedCount++;
}
} }
return !(checkedCount === 0 || checkedCount === cateResources.length); // ID
}, this.getResourceByRole(this.roleId);
isAllChecked(categoryId) { });
let cateResources = this.getResourceByCate(categoryId); },
if (cateResources == null) return false; //
let checkedCount = 0; getAllResourceCateList() {
for (let i = 0; i < cateResources.length; i++) { // API Promise
if (cateResources[i].checked === true) { listAllCate().then(response => {
checkedCount++; // allResourceCate
} this.allResourceCate = response.data;
// checked false
for (let i = 0; i < this.allResourceCate.length; i++) {
this.allResourceCate[i].checked = false;
} }
if(checkedCount===0){ //
return false; this.getAllResourceList();
});
},
// ID
getResourceByCate(categoryId) {
//
let cateResource = [];
// null
if (this.allResource == null) return null;
//
for (let i = 0; i < this.allResource.length; i++) {
//
let resource = this.allResource[i];
// ID ID
if (resource.categoryId === categoryId) {
cateResource.push(resource);
} }
return checkedCount === cateResources.length; }
}, return cateResource;
handleSave() { },
this.$confirm('是否分配资源?', '提示', { // ID
confirmButtonText: '确定', getResourceByRole(roleId) {
cancelButtonText: '取消', // API ID Promise
type: 'warning' listResourceByRole(roleId).then(response => {
}).then(() => { // allocResource 便
let checkedResourceIds = new Set(); let allocResource = response.data;
if (this.allResource != null && this.allResource.length > 0) { // getResourceChecked
this.allResource.forEach(item => {
if (item.checked) {
checkedResourceIds.add(item.id);
}
});
}
let params = new URLSearchParams();
params.append("roleId", this.roleId);
params.append("resourceIds", Array.from(checkedResourceIds));
allocResource(params).then(response => {
this.$message({
message: '分配成功',
type: 'success',
duration: 1000
});
this.$router.back();
})
})
},
handleClear() {
this.allResourceCate.forEach(item => {
item.checked = false;
});
this.allResource.forEach(item => { this.allResource.forEach(item => {
item.checked = false; item.checked = this.getResourceChecked(item.id, allocResource);
});
// isAllChecked
this.allResourceCate.forEach(item => {
item.checked = this.isAllChecked(item.id);
}); });
//
this.$forceUpdate(); this.$forceUpdate();
}, });
handleCheckAllChange(cate) { },
let cateResources = this.getResourceByCate(cate.id); // ID
for (let i = 0; i < cateResources.length; i++) { getResourceChecked(resourceId, allocResource) {
cateResources[i].checked = cate.checked; // false
if (allocResource == null || allocResource.length === 0) return false;
//
for (let i = 0; i < allocResource.length; i++) {
// ID true
if (allocResource[i].id === resourceId) {
return true;
} }
this.$forceUpdate(); }
}, return false;
handleCheckChange(resource) { },
this.allResourceCate.forEach(item=>{ //
if(item.id===resource.categoryId){ isIndeterminate(categoryId) {
item.checked = this.isAllChecked(resource.categoryId); //
let cateResources = this.getResourceByCate(categoryId);
// false
if (cateResources == null) return false;
// 0
let checkedCount = 0;
//
for (let i = 0; i < cateResources.length; i++) {
if (cateResources[i].checked === true) {
checkedCount++;
}
}
// 0 true
return!(checkedCount === 0 || checkedCount === cateResources.length);
},
//
isAllChecked(categoryId) {
//
let cateResources = this.getResourceByCate(categoryId);
// false
if (cateResources == null) return false;
// 0
let checkedCount = 0;
//
for (let i = 0; i < cateResources.length; i++) {
if (cateResources[i].checked === true) {
checkedCount++;
}
}
// 0 false
if (checkedCount === 0) {
return false;
}
// true
return checkedCount === cateResources.length;
},
//
handleSave() {
// Set ID
let checkedResourceIds = new Set();
//
if (this.allResource!= null && this.allResource.length > 0) {
// checked true ID Set
this.allResource.forEach(item => {
if (item.checked) {
checkedResourceIds.add(item.id);
} }
}); });
this.$forceUpdate();
} }
// URLSearchParams
let params = new URLSearchParams();
// ID
params.append("roleId", this.roleId);
// ID Array.from
params.append("resourceIds", Array.from(checkedResourceIds));
// API Promise
allocResource(params).then(response => {
// success 1000
this.$message({
message: '分配成功',
type: 'success',
duration: 1000
});
//
this.$router.back();
})
},
//
handleClear() {
// checked false
this.allResourceCate.forEach(item => {
item.checked = false;
});
// checked false
this.allResource.forEach(item => {
item.checked = false;
});
//
this.$forceUpdate();
},
// /
handleCheckAllChange(cate) {
//
let cateResources = this.getResourceByCate(cate.id);
//
for (let i = 0; i < cateResources.length; i++) {
cateResources[i].checked = cate.checked;
}
//
this.$forceUpdate();
},
//
handleCheckChange(resource) {
//
this.allResourceCate.forEach(item => {
if (item.id === resource.categoryId) {
//
item.checked = this.isAllChecked(resource.categoryId);
}
});
//
this.$forceUpdate();
} }
} }
}
</script> </script>
<style scoped> <style scoped>
.table-layout { /* 定义了名为 'table-layout' 的类选择器样式规则,用于设置布局相关的样式 */
padding: 20px; .table-layout {
border-left: 1px solid #DCDFE6; /* 设置内边距为 20px使内部元素有合适的间距 */
border-right: 1px solid #DCDFE6; padding: 20px;
border-bottom: 1px solid #DCDFE6; /* 设置左边框样式,颜色为 #DCDFE6线条样式为实线宽度为 1px */
} border-left: 1px solid #DCDFE6;
/* 设置右边框样式,与左边框相同 */
border-right: 1px solid #DCDFE6;
/* 设置下边框样式,与左右边框相同 */
border-bottom: 1px solid #DCDFE6;
}
.top-line { /* 定义了名为 'top-line' 的类选择器样式规则,用于给元素添加顶部边框,可能用于区分不同部分等样式效果 */
border-top: 1px solid #DCDFE6; .top-line {
} border-top: 1px solid #DCDFE6;
}
</style><style scoped>
/* 定义了名为 'table-layout' 的类选择器样式规则,用于设置布局相关的样式 */
.table-layout {
/* 设置内边距为 20px使内部元素有合适的间距 */
padding: 20px;
/* 设置左边框样式,颜色为 #DCDFE6线条样式为实线宽度为 1px */
border-left: 1px solid #DCDFE6;
/* 设置右边框样式,与左边框相同 */
border-right: 1px solid #DCDFE6;
/* 设置下边框样式,与左右边框相同 */
border-bottom: 1px solid #DCDFE6;
}
/* 定义了名为 'top-line' 的类选择器样式规则,用于给元素添加顶部边框,可能用于区分不同部分等样式效果 */
.top-line {
border-top: 1px solid #DCDFE6;
}
</style><style scoped>
/* 定义了名为 'table-layout' 的类选择器样式规则,用于设置布局相关的样式 */
.table-layout {
/* 设置内边距为 20px使内部元素有合适的间距 */
padding: 20px;
/* 设置左边框样式,颜色为 #DCDFE6线条样式为实线宽度为 1px */
border-left: 1px solid #DCDFE6;
/* 设置右边框样式,与左边框相同 */
border-right: 1px solid #DCDFE6;
/* 设置下边框样式,与左右边框相同 */
border-bottom: 1px solid #DCDFE6;
}
/* 定义了名为 'top-line' 的类选择器样式规则,用于给元素添加顶部边框,可能用于区分不同部分等样式效果 */
.top-line {
border-top: 1px solid #DCDFE6;
}
</style> </style>

@ -1,9 +1,14 @@
<template>  <template>
<!-- 整体的应用容器 div用于包裹页面内的各个功能模块 -->
<div class="app-container"> <div class="app-container">
<!-- 筛选搜索区域的卡片容器设置了阴影效果为无 -->
<el-card class="filter-container" shadow="never"> <el-card class="filter-container" shadow="never">
<div> <div>
<!-- 搜索图标元素使用了 el-icon-search 图标 -->
<i class="el-icon-search"></i> <i class="el-icon-search"></i>
<!-- 筛选搜索的文字提示 -->
<span>筛选搜索</span> <span>筛选搜索</span>
<!-- 查询搜索按钮设置了样式为右浮动按钮类型为主要primary点击时调用 handleSearchList 方法按钮大小为 small -->
<el-button <el-button
style="float:right" style="float:right"
type="primary" type="primary"
@ -11,6 +16,7 @@
size="small"> size="small">
查询搜索 查询搜索
</el-button> </el-button>
<!-- 重置按钮设置了样式为右浮动且距离右侧 15px点击时调用 handleResetSearch 方法按钮大小为 small -->
<el-button <el-button
style="float:right;margin-right: 15px" style="float:right;margin-right: 15px"
@click="handleResetSearch()" @click="handleResetSearch()"
@ -19,38 +25,52 @@
</el-button> </el-button>
</div> </div>
<div style="margin-top: 15px"> <div style="margin-top: 15px">
<!-- 内联表单绑定了 listQuery 数据对象表单大小为 small标签宽度为 140px用于设置筛选条件 -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px"> <el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单项目标签为输入搜索内部的输入框双向绑定 listQuery.keyword设置了类名占位符以及可清空功能 -->
<el-form-item label="输入搜索:"> <el-form-item label="输入搜索:">
<el-input v-model="listQuery.keyword" class="input-width" placeholder="角色名称" clearable></el-input> <el-input v-model="listQuery.keyword" class="input-width" placeholder="角色名称" clearable></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</el-card> </el-card>
<!-- 操作区域的卡片容器设置了阴影效果为无 -->
<el-card class="operate-container" shadow="never"> <el-card class="operate-container" shadow="never">
<!-- 图标元素使用了 el-icon-tickets 图标 -->
<i class="el-icon-tickets"></i> <i class="el-icon-tickets"></i>
<!-- 数据列表的文字提示 -->
<span>数据列表</span> <span>数据列表</span>
<!-- 添加按钮大小为 mini添加了自定义类名 btn-add点击时调用 handleAdd 方法设置了左侧外边距为 20px -->
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button> <el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
</el-card> </el-card>
<!-- 表格容器 div用于放置展示角色数据的表格 -->
<div class="table-container"> <div class="table-container">
<!-- el-table 组件用于展示角色数据列表设置了引用名绑定的数据表格宽度加载状态绑定以及边框等属性 -->
<el-table ref="roleTable" <el-table ref="roleTable"
:data="list" :data="list"
style="width: 100%;" style="width: 100%;"
v-loading="listLoading" border> v-loading="listLoading" border>
<!-- 表格列标签为编号设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 id 属性 -->
<el-table-column label="编号" width="100" align="center"> <el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template> <template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为角色名称内容居中对齐通过插槽作用域展示对应行数据的 name 属性 -->
<el-table-column label="角色名称" align="center"> <el-table-column label="角色名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template> <template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为描述内容居中对齐通过插槽作用域展示对应行数据的 description 属性 -->
<el-table-column label="描述" align="center"> <el-table-column label="描述" align="center">
<template slot-scope="scope">{{scope.row.description}}</template> <template slot-scope="scope">{{scope.row.description}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为用户数设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 adminCount 属性 -->
<el-table-column label="用户数" width="100" align="center"> <el-table-column label="用户数" width="100" align="center">
<template slot-scope="scope">{{scope.row.adminCount}}</template> <template slot-scope="scope">{{scope.row.adminCount}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为添加时间设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 createTime 属性并使用 formatDateTime 过滤器对时间进行格式化展示 -->
<el-table-column label="添加时间" width="160" align="center"> <el-table-column label="添加时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template> <template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
</el-table-column> </el-table-column>
<!-- 表格列标签为是否启用设置了宽度和内容居中对齐通过插槽作用域展示对应行数据的 status 属性并使用 el-switch 组件进行切换操作绑定了相关的事件和值 -->
<el-table-column label="是否启用" width="140" align="center"> <el-table-column label="是否启用" width="140" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
@ -61,6 +81,7 @@
</el-switch> </el-switch>
</template> </template>
</el-table-column> </el-table-column>
<!-- 表格列标签为操作设置了宽度和内容居中对齐通过插槽作用域展示多个操作按钮包括分配菜单分配资源编辑和删除按钮分别绑定了对应的点击事件 -->
<el-table-column label="操作" width="160" align="center"> <el-table-column label="操作" width="160" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-row> <el-row>
@ -74,21 +95,23 @@
</el-button> </el-button>
</el-row> </el-row>
<el-row> <el-row>
<el-button size="mini" <el-button size="mini"
type="text" type="text"
@click="handleUpdate(scope.$index, scope.row)"> @click="handleUpdate(scope.$index, scope.row)">
编辑 编辑
</el-button> </el-button>
<el-button size="mini" <el-button size="mini"
type="text" type="text"
@click="handleDelete(scope.$index, scope.row)">删除 @click="handleDelete(scope.$index, scope.row)">删除
</el-button> </el-button>
</el-row> </el-row>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 分页容器 div用于放置分页组件 -->
<div class="pagination-container"> <div class="pagination-container">
<!-- el-pagination 分页组件设置了背景色相关页面切换事件绑定布局样式当前页每页数量可选每页数量数组以及总记录数等属性 -->
<el-pagination <el-pagination
background background
@size-change="handleSizeChange" @size-change="handleSizeChange"
@ -100,22 +123,27 @@
:total="total"> :total="total">
</el-pagination> </el-pagination>
</div> </div>
<!-- el-dialog 对话框组件根据 isEdit 变量动态设置标题为编辑角色添加角色绑定了显示状态设置了宽度 -->
<el-dialog <el-dialog
:title="isEdit?'编辑角色':'添加角色'" :title="isEdit?'编辑角色':'添加角色'"
:visible.sync="dialogVisible" :visible.sync="dialogVisible"
width="40%"> width="40%">
<!-- 对话框内的表单绑定了 role 数据对象设置了表单引用标签宽度和大小 -->
<el-form :model="role" <el-form :model="role"
ref="roleForm" ref="roleForm"
label-width="150px" size="small"> label-width="150px" size="small">
<!-- 表单项目标签为角色名称输入框双向绑定 role.name并设置了宽度 -->
<el-form-item label="角色名称:"> <el-form-item label="角色名称:">
<el-input v-model="role.name" style="width: 250px"></el-input> <el-input v-model="role.name" style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为描述输入框双向绑定 role.description设置了文本域类型和行数以及宽度 -->
<el-form-item label="描述:"> <el-form-item label="描述:">
<el-input v-model="role.description" <el-input v-model="role.description"
type="textarea" type="textarea"
:rows="5" :rows="5"
style="width: 250px"></el-input> style="width: 250px"></el-input>
</el-form-item> </el-form-item>
<!-- 表单项目标签为是否启用内部是一个单选按钮组双向绑定 role.status用于选择角色是否启用的状态 -->
<el-form-item label="是否启用:"> <el-form-item label="是否启用:">
<el-radio-group v-model="role.status"> <el-radio-group v-model="role.status">
<el-radio :label="1"></el-radio> <el-radio :label="1"></el-radio>
@ -123,6 +151,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 对话框底部的按钮区域通过插槽定义了取消和确定按钮分别绑定了对应的点击事件 -->
<span slot="footer" class="dialog-footer"> <span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button> <el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button> <el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
@ -131,156 +160,199 @@
</div> </div>
</template> </template>
<script> <script>
import {fetchList,createRole,updateRole,updateStatus,deleteRole} from '@/api/role'; // '@/api/role'API
import {formatDate} from '@/utils/date'; import {fetchList, createRole, updateRole, updateStatus, deleteRole} from '@/api/role';
// '@/utils/date'formatDate
import {formatDate} from '@/utils/date';
// 15null
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
keyword: null
};
// IDIDnull01
const defaultRole = {
id: null,
name: null,
description: null,
adminCount: 0,
status: 1
};
const defaultListQuery = { export default {
pageNum: 1, name: 'roleList',
pageSize: 5, data() {
keyword: null return {
}; // Object.assigndefaultListQuery
const defaultRole = { listQuery: Object.assign({}, defaultListQuery),
id: null, // nullAPI
name: null, list: null,
description: null, // nullAPI
adminCount: 0, total: null,
status: 1 // truefalsefalse
}; listLoading: false,
export default { // truefalsefalse
name: 'roleList', dialogVisible: false,
data() { // defaultRole
return { role: Object.assign({}, defaultRole),
listQuery: Object.assign({}, defaultListQuery), // truefalsefalse
list: null, isEdit: false
total: null, }
listLoading: false, },
dialogVisible: false, //
role: Object.assign({}, defaultRole), created() {
isEdit: false this.getList();
},
filters: {
// time
formatDateTime(time) {
// null'N/A'
if (time == null || time === '') {
return 'N/A';
} }
// JavaScriptDate
let date = new Date(time);
// formatDate'yyyy-MM-dd hh:mm:ss'
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
},
methods: {
//
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
}, },
created() { // 1
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList(); this.getList();
}, },
filters: { // 1val
formatDateTime(time) { handleSizeChange(val) {
if (time == null || time === '') { this.listQuery.pageNum = 1;
return 'N/A'; this.listQuery.pageSize = val;
} this.getList();
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
}, },
methods: { // val
handleResetSearch() { handleCurrentChange(val) {
this.listQuery = Object.assign({}, defaultListQuery); this.listQuery.pageNum = val;
}, this.getList();
handleSearchList() { },
this.listQuery.pageNum = 1; //
this.getList(); handleAdd() {
}, this.dialogVisible = true;
handleSizeChange(val) { this.isEdit = false;
this.listQuery.pageNum = 1; this.role = Object.assign({}, defaultRole);
this.listQuery.pageSize = val; },
this.getList(); //
}, handleStatusChange(index, row) {
handleCurrentChange(val) { this.$confirm('是否要修改该状态?', '提示', {
this.listQuery.pageNum = val; confirmButtonText: '确定',
this.getList(); cancelButtonText: '取消',
}, type: 'warning'
handleAdd() { }).then(() => {
this.dialogVisible = true; // updateStatusID
this.isEdit = false; updateStatus(row.id, {status: row.status}).then(response => {
this.role = Object.assign({},defaultRole); this.$message({
}, type: 'success',
handleStatusChange(index, row) { message: '修改成功!'
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id, {status: row.status}).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
});
}); });
}).catch(() => { });
}).catch(() => {
//
this.$message({
type: 'info',
message: '取消修改'
});
this.getList();
});
},
//
handleDelete(index, row) {
this.$confirm('是否要删除该角色?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let ids = [];
// ID
ids.push(row.id);
let params = new URLSearchParams();
// IDURLSearchParamsAPI
params.append("ids", ids);
// deleteRole
deleteRole(params).then(response => {
this.$message({ this.$message({
type: 'info', type: 'success',
message: '取消修改' message: '删除成功!'
}); });
this.getList(); this.getList();
}); });
}, });
handleDelete(index, row) { },
this.$confirm('是否要删除该角色?', '提示', { //
confirmButtonText: '确定', handleUpdate(index, row) {
cancelButtonText: '取消', this.dialogVisible = true;
type: 'warning' this.isEdit = true;
}).then(() => { this.role = Object.assign({}, row);
let ids = []; },
ids.push(row.id); //
let params=new URLSearchParams(); handleDialogConfirm() {
params.append("ids",ids); this.$confirm('是否要确认?', '提示', '提示', {
deleteRole(params).then(response => { confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// updateRoleID
if (this.isEdit) {
updateRole(this.role.id, this.role).then(response => {
this.$message({ this.$message({
type: 'success', message: '修改成功!',
message: '删除成功!' type: 'success'
}); });
this.dialogVisible = false;
this.getList(); this.getList();
}); })
}); } else {
}, // createRole
handleUpdate(index, row) { createRole(this.role).then(response => {
this.dialogVisible = true; this.$message({
this.isEdit = true; message: '添加成功!',
this.role = Object.assign({},row); type: 'success'
}, });
handleDialogConfirm() { this.dialogVisible = false;
this.$confirm('是否要确认?', '提示', { this.getList();
confirmButtonText: '确定', })
cancelButtonText: '取消', }
type: 'warning' })
}).then(() => { },
if (this.isEdit) { // '/ums/allocMenu'ID
updateRole(this.role.id,this.role).then(response => { handleSelectMenu(index, row) {
this.$message({ this.$router.push({path: '/ums/allocMenu', query: {roleId: row.id}})
message: '修改成功!', },
type: 'success' // '/ums/allocResource'ID
}); handleSelectResource(index, row) {
this.dialogVisible =false; this.$router.push({path: '/ums/allocResource', query: {roleId: row.id}})
this.getList(); },
}) // fetchList
} else { getList() {
createRole(this.role).then(response => { this.listLoading = true;
this.$message({ fetchList(this.listQuery).then(response => {
message: '添加成功!', this.listLoading = false;
type: 'success' this.list = response.data.list;
}); this.total = response.data.total;
this.dialogVisible =false; });
this.getList();
})
}
})
},
handleSelectMenu(index,row){
this.$router.push({path:'/ums/allocMenu',query:{roleId:row.id}})
},
handleSelectResource(index,row){
this.$router.push({path:'/ums/allocResource',query:{roleId:row.id}})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
}
} }
} }
}
</script> </script>
<style></style> <style>
/* CSS updateMenu
来对组件的外观进行定制化显示 */
/* CSS resourceList
- 设置表格的样式如表头单元格的字体颜色边框样式等
- 对筛选区域操作按钮分页组件等元素的布局外观进行调整比如按钮的颜色大小间距等
- 定制对话框的样式像对话框的背景色边框按钮在对话框内的排版等
通过添加这些样式规则可以让页面的显示更加符合设计要求和美观性 */
</style>

Loading…
Cancel
Save