Compare commits

...

9 Commits

@ -0,0 +1,12 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"]
}

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

14
.gitignore vendored

@ -0,0 +1,14 @@
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

@ -0,0 +1,10 @@
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2018-2024] [macrozheng]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

@ -1,2 +1,83 @@
# fourth
# mall-admin-web
<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-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>
</p>
## 前言
该项目为前后端分离项目的前端部分,后端项目`mall`地址:[传送门](https://github.com/macrozheng/mall) 。
## 项目介绍
`mall-admin-web`是一个电商后台管理系统的前端项目基于Vue+Element实现。主要包括商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等功能。
### 项目演示
项目在线演示地址:[https://www.macrozheng.com/admin/](https://www.macrozheng.com/admin/)
![后台管理系统功能演示](http://img.macrozheng.com/mall/project/mall_admin_show.png)
### 技术选型
| 技术 | 说明 | 官网 |
| ----------------- | --------------------- | ------------------------------------------------------------ |
| Vue | 前端框架 | [https://vuejs.org/](https://vuejs.org/) |
| Vue-router | 路由框架 | [https://router.vuejs.org/](https://router.vuejs.org/) |
| Vuex | 全局状态管理框架 | [https://vuex.vuejs.org/](https://vuex.vuejs.org/) |
| Element | 前端UI框架 | [https://element.eleme.io/](https://element.eleme.io/) |
| Axios | 前端HTTP框架 | [https://github.com/axios/axios](https://github.com/axios/axios) |
| v-charts | 基于Echarts的图表框架 | [https://v-charts.js.org/](https://v-charts.js.org/) |
| Js-cookie | cookie管理工具 | [https://github.com/js-cookie/js-cookie](https://github.com/js-cookie/js-cookie) |
| nprogress | 进度条控件 | [https://github.com/rstacruz/nprogress](https://github.com/rstacruz/nprogress) |
| vue-element-admin | 项目脚手架参考 | [https://github.com/PanJiaChen/vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) |
### 项目布局
``` lua
src -- 源码目录
├── api -- axios网络请求定义
├── assets -- 静态图片资源文件
├── components -- 通用组件封装
├── icons -- svg矢量图片文件
├── router -- vue-router路由配置
├── store -- vuex的状态管理
├── styles -- 全局css样式
├── utils -- 工具类
└── views -- 前端页面
├── home -- 首页
├── layout -- 通用页面加载框架
├── login -- 登录页
├── oms -- 订单模块页面
├── pms -- 商品模块页面
└── sms -- 营销模块页面
```
## 搭建步骤
- 下载node并安装[https://nodejs.org/dist/v12.14.0/node-v12.14.0-x64.msi](https://nodejs.org/dist/v12.14.0/node-v12.14.0-x64.msi);
- 该项目为前后端分离项目,访问本地访问接口需搭建后台环境,搭建请参考后端项目[传送门](https://github.com/macrozheng/mall);
- 访问在线接口无需搭建后台环境,只需将`config/dev.env.js`文件中的`BASE_API`改为[https://admin-api.macrozheng.com](https://admin-api.macrozheng.com)即可;
- 如果你对接的是[mall-swarm](https://github.com/macrozheng/mall-swarm) 微服务后台的话,所有接口都需要通过网关访问,需要将`config/dev.env.js`文件中的`BASE_API`改为[http://localhost:8201/mall-admin](http://localhost:8201/mall-admin)
- 克隆源代码到本地使用IDEA打开并完成编译;
- 在IDEA命令行中运行命令`npm install`,下载相关依赖;
- 在IDEA命令行中运行命令`npm run dev`,运行项目;
- 访问地址:[http://localhost:8090](http://localhost:8090) 即可打开后台管理系统页面;
- 具体部署过程请参考:[mall前端项目的安装与部署](https://www.macrozheng.com/mall/deploy/mall_deploy_web.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)
Copyright (c) 2018-2024 macrozheng

@ -0,0 +1,41 @@
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})

@ -0,0 +1,54 @@
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

@ -0,0 +1,101 @@
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}

@ -0,0 +1,22 @@
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}

@ -0,0 +1,91 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/icons')],
options: {
symbolId: 'icon-[name]'
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude: resolve('src/icons'),
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}

@ -0,0 +1,95 @@
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})

@ -0,0 +1,145 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

@ -0,0 +1,8 @@
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
BASE_API: '"http://localhost:8080"'
})

@ -0,0 +1,69 @@
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8090, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: './',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}

@ -0,0 +1,5 @@
'use strict'
module.exports = {
NODE_ENV: '"production"',
BASE_API: '"https://admin-api.macrozheng.com"'
}

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>mall-admin-web</title>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-73YJPXJTLX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-73YJPXJTLX');
</script>
<!--Baidu Analytics-->
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?8b02a318fde5831da10426656a43d03c";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
</head>
<body>
<script src="./static/tinymce4.7.5/tinymce.min.js"></script>
<!--<script src="https://unpkg.com/tinymce@4.7.5/tinymce.min.js"></script>-->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

@ -0,0 +1,114 @@
<template>
<div class="app-wrapper" :class="classObj">
<sidebar class="sidebar-container"></sidebar>
<div class="main-container">
<navbar></navbar>
<app-main></app-main>
</div>
</div>
</template>
<script>
import { Navbar, Sidebar, AppMain } from './components'
import ResizeMixin from './mixin/ResizeHandler'
export default {
name: 'layout',
components: {
Navbar,
Sidebar,
AppMain
},
mixins: [ResizeMixin],
computed: {
sidebar() {
return this.$store.state.app.sidebar
},
device() {
return this.$store.state.app.device
},
classObj() {
return {
hideSidebar: !this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device === 'mobile'
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
}
</style>
#abc
<template>
<!-- 最外层的 `div` 元素类名为 `app-wrapper`并且通过 `:class="classObj"` 进行动态类名绑定根据 `classObj` 计算属性返回的对象来决定应用哪些类名用于实现根据不同条件动态改变样式的功能 -->
<div class="app-wrapper" :class="classObj">
<!-- 使用名为 `sidebar` 的自定义组件类名为 `sidebar-container`通常这个组件用于展示页面的侧边栏内容比如导航菜单等功能模块 -->
<sidebar class="sidebar-container"></sidebar>
<!-- 一个 `div` 元素类名为 `main-container`作为主要内容区域的容器用于包裹页面中除侧边栏之外的核心展示内容 -->
<div class="main-container">
<!-- 使用名为 `navbar` 的自定义组件用于展示页面顶部的导航栏包含诸如页面切换功能操作入口等元素 -->
<navbar></navbar>
<!-- 使用名为 `app-main` 的自定义组件用于承载页面的主体内容例如数据展示操作表单等具体业务相关的内容展示 -->
<app-main></app-main>
</div>
</div>
</template>
<script>
// `./components` `Navbar``Sidebar``AppMain`
import { Navbar, Sidebar, AppMain } from './components';
// `./mixin/ResizeHandler` `ResizeMixin``mixins` Vue.js `ResizeMixin` 使
import ResizeMixin from './mixin/ResizeHandler';
export default {
name: 'layout',
components: {
// `components` `Navbar``Sidebar``AppMain` 使 Vue.js 使
Navbar,
Sidebar,
AppMain
},
mixins: [ResizeMixin],
computed: {
sidebar() {
// `sidebar`访 Vuex `$store` `app` `sidebar` 使
return this.$store.state.app.sidebar;
},
device() {
// `device` `$store` `app` `mobile`便
return this.$store.state.app.device;
},
classObj() {
// `classObj` `hideSidebar` `!this.sidebar.opened``withoutAnimation` `this.sidebar.withoutAnimation``mobile` `this.device === 'mobile'`
return {
hideSidebar:!this.sidebar.opened,
withoutAnimation: this.sidebar.withoutAnimation,
mobile: this.device ==='mobile'
};
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
// 使 `@import` `src/styles/mixin.scss` SCSS `mixin`便
@import "src/styles/mixin.scss";
.app-wrapper {
// 使 `clearfix` `mixin.scss` `clearfix`
@include clearfix;
// `relative`便
position: relative;
// `100%`
height: 100%;
// `100%`使
width: 100%;
}
</style>

@ -0,0 +1,43 @@
<template>
<section class="app-main">
<transition name="fade" mode="out-in">
<!-- <router-view :key="key"></router-view> -->
<router-view></router-view>
</transition>
</section>
</template>
<script>
export default {
name: 'AppMain',
computed: {
// key() {
// return this.$route.name !== undefined ? this.$route.name + +new Date() : this.$route + +new Date()
// }
}
}
</script>
#abc
<template>
<!-- 使用 `section` 元素作为容器类名为 `app-main`通常这个元素会用于承载页面的主体内容部分在整个页面布局结构中扮演重要角色例如展示各种业务模块数据展示区域等 -->
<section class="app-main">
<!-- 使用 Vue.js `<transition>` 组件设置过渡效果的名称为 `fade`过渡模式为 `out-in`这种过渡模式意味着旧元素先执行离开过渡淡出完成后新元素再执行进入过渡淡入用于在路由视图切换时不同页面组件切换时实现平滑的过渡动画效果 -->
<transition name="fade" mode="out-in">
<!-- 原本此处使用 `:key` 绑定了一个计算属性 `key`目前代码中该计算属性被注释掉了用于给 `<router-view>` 组件添加一个唯一标识当路由变化且 `key` 值改变时会强制重新渲染 `<router-view>` 对应的组件实现更有效的组件更新和过渡效果展示现在虽然没有绑定 `key` `<router-view>` 组件依然用于渲染与当前路由匹配的组件内容也就是展示具体的页面内容部分 -->
<router-view></router-view>
</transition>
</section>
</template>
<script>
export default {
name: 'AppMain',
computed: {
// `key` `<router-view>` `key`
// `this.$route.name` `key` `$route.name` `key` `$route.name` `+new Date()` `key` `<router-view>` `$route.name` `$route` `key` `<transition>`
// key() {
// return this.$route.name!== undefined? this.$route.name + +new Date() : this.$route + +new Date()
// }
}
}
</script>

@ -0,0 +1,235 @@
<template>
<el-menu class="navbar" mode="horizontal">
<hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<breadcrumb></breadcrumb>
<el-dropdown class="avatar-container" trigger="click">
<div class="avatar-wrapper">
<img class="user-avatar" :src="avatar">
<i class="el-icon-caret-bottom"></i>
</div>
<el-dropdown-menu class="user-dropdown" slot="dropdown">
<router-link class="inlineBlock" to="/">
<el-dropdown-item>
首页
</el-dropdown-item>
</router-link>
<el-dropdown-item divided>
<span @click="logout" style="display:block;">退出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-menu>
</template>
<script>
import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
export default {
components: {
Breadcrumb,
Hamburger
},
computed: {
...mapGetters([
'sidebar',
'avatar'
])
},
methods: {
toggleSideBar() {
this.$store.dispatch('ToggleSideBar')
},
logout() {
this.$store.dispatch('LogOut').then(() => {
location.reload() // vue-router bug
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.navbar {
height: 50px;
line-height: 50px;
border-radius: 0px !important;
.hamburger-container {
line-height: 58px;
height: 50px;
float: left;
padding: 0 10px;
}
.screenfull {
position: absolute;
right: 90px;
top: 16px;
color: red;
}
.avatar-container {
height: 50px;
display: inline-block;
position: absolute;
right: 35px;
.avatar-wrapper {
cursor: pointer;
margin-top: 5px;
position: relative;
.user-avatar {
width: 40px;
height: 40px;
border-radius: 10px;
}
.el-icon-caret-bottom {
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
</style>
#abc
<template>
<!-- 使用 Element UI `el-menu` 组件创建一个水平模式`mode="horizontal"`的导航栏类名为 `navbar`用于构建页面顶部的导航菜单结构 -->
<el-menu class="navbar" mode="horizontal">
<!-- 使用名为 `hamburger` 的自定义组件类名为 `hamburger-container`通过属性绑定传递 `toggleClick` 方法用于切换侧边栏显示状态以及 `isActive` 属性与侧边栏是否打开状态相关通常这个组件会以汉堡菜单图标形式呈现点击可展开或收起侧边栏 -->
<hamburger class="hamburger-container" :toggleClick="toggleSideBar" :isActive="sidebar.opened"></hamburger>
<!-- 使用名为 `breadcrumb` 的自定义组件用于展示面包屑导航显示当前页面在整个页面层级结构中的位置路径信息方便用户知晓所在页面的层级关系 -->
<breadcrumb></breadcrumb>
<!-- 使用 Element UI `el-dropdown` 下拉菜单组件类名为 `avatar-container`设置触发下拉菜单的方式为点击`trigger="click"`用于创建一个包含用户头像等相关操作选项的下拉菜单 -->
<el-dropdown class="avatar-container" trigger="click">
<!-- 一个 `div` 元素类名为 `avatar-wrapper`作为用户头像及其相关元素的容器用于包裹头像图片和下拉箭头图标等 -->
<div class="avatar-wrapper">
<!-- 使用 `img` 图片元素通过 `:src` 绑定 `avatar` 变量在计算属性中通过 Vuex `getters` 获取用于显示用户头像图片资源设置类名为 `user-avatar`用于展示用户的头像 -->
<img class="user-avatar" :src="avatar">
<!-- 使用 Element UI 的图标组件 `el-icon-caret-bottom`展示一个向下的箭头图标用于提示用户该区域可点击展开下拉菜单增强视觉提示效果 -->
<i class="el-icon-caret-bottom"></i>
</div>
<!-- `el-dropdown-menu` 组件类名为 `user-dropdown`通过 `slot="dropdown"` 插槽与外层的 `el-dropdown` 组件关联用于定义下拉菜单中具体的菜单项内容 -->
<el-dropdown-menu class="user-dropdown" slot="dropdown">
<!-- 使用 `router-link` 组件创建一个路由链接类名为 `inlineBlock`链接到根路径`/`点击可导航到首页并且将 `el-dropdown-item` 作为子元素包裹在其中用于在下拉菜单中展示首页这一菜单项 -->
<router-link class="inlineBlock" to="/">
<el-dropdown-item>
首页
</el-dropdown-item>
</router-link>
<!-- `el-dropdown-item` 组件设置了 `divided` 属性可能用于添加分割线样式区分不同菜单项内部包含一个 `span` 元素点击该 `span` 元素会触发 `logout` 方法用于执行退出登录的操作在下拉菜单中展示退出这一菜单项 -->
<el-dropdown-item divided>
<span @click="logout" style="display:block;">退出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-menu>
</template>
<script>
// `vuex` `mapGetters` Vuex `getters` 便 Vuex
import { mapGetters } from 'vuex';
// `@/components/` `Breadcrumb`
import Breadcrumb from '@/components/Breadcrumb';
// `@/components/` `Hamburger`
import Hamburger from '@/components/Hamburger';
export default {
components: {
// `components` `Breadcrumb` `Hamburger` 使 Vue.js 使
Breadcrumb,
Hamburger
},
computed: {
// 使 `mapGetters` Vuex `sidebar` `avatar` `getters` `this.sidebar` `this.avatar` `sidebar` `avatar`
...mapGetters([
'sidebar',
'avatar'
])
},
methods: {
toggleSideBar() {
// `toggleSideBar` `$store.dispatch` Vuex `ToggleSideBar` `action` Vuex `store`
this.$store.dispatch('ToggleSideBar')
},
logout() {
// `logout` 退 `$store.dispatch` Vuex `LogOut` `store` `LogOut` `.then` `location.reload` `vue-router` `bug`退
this.$store.dispatch('LogOut').then(() => {
location.reload(); // vue-router bug
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
// `navbar`
.navbar {
// `50px`使
height: 50px;
// `50px`使
line-height: 50px;
// `el-menu` `0px`使`!important`
border-radius: 0px!important;
// `hamburger-container`
.hamburger-container {
// `58px`使
line-height: 58px;
// `50px`
height: 50px;
// 使便
float: left;
// `0 10px`
padding: 0 10px;
}
// `screenfull` 使
.screenfull {
// `absolute`使便
position: absolute;
// `90px` 使
right: 90px;
// `16px`
top: 16px;
//
color: red;
}
// `avatar-container`
.avatar-container {
// `50px`使
height: 50px;
// `inline-block`使
display: inline-block;
// `absolute`便便
position: absolute;
// `35px` 使
right: 35px;
// `avatar-wrapper`
.avatar-wrapper {
// `cursor: pointer`
cursor: pointer;
// `5px`使
margin-top: 5px;
// `relative`便
position: relative;
// `user-avatar`
.user-avatar {
// `40px`使
width: 40px;
// `40px`
height: 40px;
// `10px`使
border-radius: 10px;
}
// `el-icon-caret-bottom`
.el-icon-caret-bottom {
// `absolute`使 `avatar-wrapper`
position: absolute;
// `-20px` 使
right: -20px;
// `25px` 使
top: 25px;
// `12px`使
font-size: 12px;
}
}
}
}
</style>

@ -0,0 +1,154 @@
<template>
<div class="menu-wrapper">
<template v-for="item in routes" v-if="!item.hidden&&item.children">
<router-link v-if="hasOneShowingChildren(item.children) && !item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path"
:key="item.children[0].name">
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
<span v-if="item.children[0].meta&&item.children[0].meta.title" slot="title">{{item.children[0].meta.title}}</span>
</el-menu-item>
</router-link>
<el-submenu v-else :index="item.name||item.path" :key="item.name">
<template slot="title">
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
<span v-if="item.meta&&item.meta.title" slot="title">{{item.meta.title}}</span>
</template>
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<!--支持外链功能-->
<a v-else-if="child.path.startsWith('http')" v-bind:href="child.path" target="_blank" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
<svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
<span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
</el-menu-item>
</a>
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
<svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
<span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
</el-menu-item>
</router-link>
</template>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: 'SidebarItem',
props: {
routes: {
type: Array
},
isNest: {
type: Boolean,
default: false
}
},
methods: {
hasOneShowingChildren(children) {
const showingChildren = children.filter(item => {
return !item.hidden
})
if (showingChildren.length === 1) {
return true
}
return false
}
}
}
</script>
#abc
<template>
<!-- 最外层的 `div` 元素类名为 `menu-wrapper`它充当整个侧边栏菜单的包裹容器起到组织和定位内部菜单元素的作用使得菜单在页面布局中有一个明确的范围和样式框架 -->
<div class="menu-wrapper">
<!-- 使用 `v-for` 指令对 `routes` 数组进行循环遍历`routes` 数组应该是从父组件传入的数据包含了侧边栏菜单各个层级的路由配置信息等内容同时结合 `v-if` 指令进行条件判断只有当当前遍历到的 `item` 元素的 `hidden` 属性为 `false`意味着该菜单项需要显示出来而非隐藏并且 `item` 具有 `children` 属性表示这个菜单项包含子菜单才会渲染内部对应的菜单结构内容以此实现根据数据动态生成侧边栏菜单的功能有选择性地展示有效的菜单项及其子菜单 -->
<template v-for="item in routes" v-if="!item.hidden&&item.children">
<!-- 使用 `router-link` 组件创建一个路由链接这是 Vue Router 用于实现页面导航的组件通过 `v-if` 指令添加了复杂的条件判断只有当满足以下几个条件时才会渲染这个路由链接
- `hasOneShowingChildren(item.children)` 方法返回 `true`即当前菜单项`item`的子菜单数组`item.children`经过筛选后只有一个需要显示的子项`hasOneShowingChildren` 方法会在后续的 `methods` 部分详细介绍作用是筛选出未隐藏的子项并判断数量是否为 1
- `!item.children[0].children` `true`也就是当前菜单项的第一个子菜单项`item.children[0]`不存在它自己的子菜单说明是一个没有下一级嵌套的子菜单项
- `!item.alwaysShow` `true`可能表示该菜单项不是那种始终要显示特定链接形式的特殊菜单项具体含义需结合整体业务逻辑判断
`:to` 属性绑定了一个拼接后的路由路径由当前菜单项`item` `path` 属性和它第一个子菜单项`item.children[0]` `path` 属性通过 `+` 号拼接而成用于指定点击这个路由链接后要跳转到的页面路径`:key` 属性绑定了第一个子菜单项`item.children[0]` `name` 属性 Vue 的虚拟 DOM 渲染机制中`key` 可以帮助更高效准确地识别每个列表项这里的路由链接作为一种列表项便于在数据变化时进行精准的更新复用以及重新排序等操作优化渲染性能 -->
<router-link v-if="hasOneShowingChildren(item.children) &&!item.children[0].children&&!item.alwaysShow" :to="item.path+'/'+item.children[0].path"
:key="item.children[0].name">
<!-- 使用 Element UI `el-menu-item` 组件来表示一个菜单项它是侧边栏菜单中的具体可点击选项`:index` 属性绑定了同样是由当前菜单项`item` `path` 和其第一个子菜单项`item.children[0]` `path` 拼接而成的字符串这个 `index` 属性在菜单的选中状态管理等方面可能会起到标识作用例如确定哪个菜单项当前处于激活状态等`:class` 属性通过对象语法绑定了一个类名控制逻辑根据 `!isNest``isNest` 是从父组件传入的布尔类型属性用于判断当前菜单项是否处于嵌套菜单的情境中默认值为 `false`的值来决定是否添加 `submenu-title-noDropdown` 类名从而实现根据不同的菜单层级或状态应用不同的样式比如改变外观样式以体现是否是嵌套菜单下的标题样式等 -->
<el-menu-item :index="item.path+'/'+item.children[0].path" :class="{'submenu-title-noDropdown':!isNest}">
<!-- 使用 `svg-icon` 自定义组件从代码上下文推测应该是用于展示 SVG 格式的图标通过 `v-if` 指令进行条件判断只有当当前菜单项的第一个子菜单项`item.children[0]` `meta` 对象中存在 `icon` 属性通常 `meta` 对象用于存放菜单项的一些额外元数据比如图标标题等相关信息才会将该 `icon` 属性的值作为图标类名传递给 `svg-icon` 组件进而展示对应的图标用于增强菜单项的可视化效果让用户更直观地识别菜单项对应的功能或页面类型等 -->
<svg-icon v-if="item.children[0].meta&&item.children[0].meta.icon" :icon-class="item.children[0].meta.icon"></svg-icon>
<!-- 使用 `span` 元素来展示菜单项的标题文本内容同样通过 `v-if` 指令判断只有当当前菜单项的第一个子菜单项`item.children[0]` `meta` 对象中存在 `title` 属性时才会将该 `title` 属性的值作为文本内容渲染到 `span` 元素中显示在菜单项上明确告知用户该菜单项所代表的含义或对应的页面名称等信息通过 `slot="title"` 属性将这个 `span` 元素放置在 `el-menu-item` 组件指定的标题插槽位置确保文本正确显示在菜单项的合适位置上 -->
<span v-if="item.children[0].meta&&item.children[0].meta.title" slot="title">{{item.children[0].meta.title}}</span>
</el-menu-item>
</router-link>
<!-- 使用 Element UI `el-submenu` 组件来创建一个包含子菜单的菜单项容器通过 `v-else` 指令意味着当不满足上面 `router-link` 组件的渲染条件时即当前菜单项的子菜单情况不符合前面所描述的那种只有一个无子菜单的显示子项的情况就会渲染这个 `el-submenu` 组件用于展示更复杂的具有多个子菜单项或者嵌套子菜单的菜单结构`:index` 属性绑定了当前菜单项`item` `name` 属性或者 `path` 属性优先使用 `name`如果 `name` 不存在则使用 `path`用于在菜单体系中标识这个子菜单的唯一性便于菜单状态管理展开与折叠控制等操作能准确对应到这个子菜单上`:key` 属性绑定了当前菜单项`item` `name` 属性同样是为了在 Vue 的虚拟 DOM 渲染机制中给这个组件实例一个唯一标识方便进行高效的更新和复用操作提升渲染性能 -->
<el-submenu v-else :index="item.name||item.path" :key="item.name">
<!-- 使用 `template` 元素并通过 `slot="title"` 属性定义了 `el-submenu` 组件的标题部分内容的插槽用于自定义子菜单标题的展示样式和内容包括图标和标题文本等信息这些内容会展示在子菜单展开按钮等位置方便用户直观地识别这个子菜单所代表的功能分类或相关页面集合等 -->
<template slot="title">
<!-- 与前面类似使用 `svg-icon` 自定义组件展示图标通过 `v-if` 指令判断当前菜单项`item` `meta` 对象中是否存在 `icon` 属性如果存在则将该 `icon` 属性的值作为图标类名传递给 `svg-icon` 组件展示对应的图标增强子菜单标题的可视化效果帮助用户快速识别该子菜单相关信息 -->
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
<!-- 使用 `span` 元素展示子菜单标题的文本内容通过 `v-if` 指令判断当前菜单项`item` `meta` 对象中是否存在 `title` 属性如果存在则将该 `title` 属性的值作为文本渲染到 `span` 元素中显示在子菜单标题位置明确告知用户这个子菜单所涉及的内容范围等信息通过 `slot="title"` 将这个 `span` 元素放置在 `template` 元素指定的标题插槽位置确保文本正确显示在子菜单标题的合适位置上 -->
<span v-if="item.meta&&item.meta.title" slot="title">{{item.meta.title}}</span>
</template>
<!-- 再次使用 `v-for` 指令对当前菜单项`el-submenu` 对应的 `item` `children` 数组即子菜单的各个子项进行循环遍历同时结合 `v-if` 指令进行条件判断只有当子项的 `hidden` 属性为 `false`表示该子项需要显示出来而非隐藏才会渲染内部对应的菜单相关组件用于动态生成子菜单下的具体菜单项内容实现有选择性地展示有效的子菜单项过滤掉不需要显示的子项 -->
<template v-for="child in item.children" v-if="!child.hidden">
<!-- 使用名为 `sidebar-item` 的自定义组件也就是当前正在定义的这个组件自身通过递归调用自身来处理嵌套菜单的情况通过 `:is-nest="true"` 属性传递一个布尔值表示当前处于嵌套菜单的状态因为这里是在处理子菜单中的子菜单项属于嵌套层级情况同时添加 `nest-menu` 类名从代码结构推测这个类名应该是用于应用一些特定的样式实现嵌套菜单相关的外观样式控制比如缩进字体大小调整等具体样式定义需查看对应的 CSS 代码通过 `:routes="[child]"` 属性将当前子项`child`作为一个只包含一个元素的数组传递给 `sidebar-item` 组件目的是让 `sidebar-item` 组件以这个子项为基础数据递归地处理它可能存在的子菜单情况如果 `child` 本身也有 `children` 属性即还有下一层级的子菜单的话`:key` 属性绑定当前子项`child` `path` 属性用于在 Vue 的虚拟 DOM 渲染机制中给这个 `sidebar-item` 组件实例一个唯一标识便于进行高效的更新复用以及重新排序等操作优化渲染性能实现多级嵌套菜单的递归构建和渲染功能 -->
<sidebar-item :is-nest="true" class="nest-menu" v-if="child.children&&child.children.length>0" :routes="[child]" :key="child.path"></sidebar-item>
<!-- 使用 `a` 元素超链接来创建一个外部链接菜单项通过 `v-else-if` 指令进行条件判断当当前子项`child` `path` 属性是以 `http` 开头时表明这是一个指向外部网页的链接就会渲染这个 `a` 元素通过 `v-bind:href` 指令将当前子项`child` `path` 属性绑定为超链接的 `href` 属性值即链接地址同时设置 `target="_blank"` 属性使得点击这个链接时会在新的浏览器标签页中打开对应的外部网页`:key` 属性绑定当前子项`child` `name` 属性用于在 Vue 的虚拟 DOM 渲染机制中给这个 `a` 元素一个唯一标识方便进行高效的更新操作确保在数据变化时能正确渲染这个外部链接菜单项 -->
<a v-else-if="child.path.startsWith('http')" v-bind:href="child.path" target="_blank" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
<svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
<span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
</el-menu-item>
</a>
<!-- 使用 `router-link` 组件创建一个内部路由链接菜单项通过 `v-else` 指令当不满足上面 `sidebar-item` `a` 元素的渲染条件时即既不是嵌套菜单情况也不是外部链接情况而是内部页面路由链接的常规情况就会渲染这个 `router-link` 组件`:to` 属性绑定了由当前菜单项`el-submenu` 对应的 `item` `path` 属性和当前子项`child` `path` 属性通过 `+` 号拼接而成的字符串作为路由路径用于指定点击这个路由链接后要跳转到的内部页面路径`:key` 属性绑定当前子项`child` `name` 属性用于在 Vue 的虚拟 DOM 渲染机制中给这个 `router-link` 组件一个唯一标识方便进行高效的更新操作确保在数据变化时能正确渲染这个内部路由链接菜单项实现内部页面之间的导航功能 -->
<router-link v-else :to="item.path+'/'+child.path" :key="child.name">
<el-menu-item :index="item.path+'/'+child.path">
<svg-icon v-if="child.meta&&child.meta.icon" :icon-class="child.meta.icon"></svg-icon>
<span v-if="child.meta&&child.meta.title" slot="title">{{child.meta.title}}</span>
</el-menu-item>
</router-link>
</template>
</el-submenu>
</template>
</div>
</template>
<script>
export default {
name: 'SidebarItem',
props: {
// `routes` `Array` `path``meta``children`
routes: {
type: Array
},
// `isNest` `Boolean` `false`
isNest: {
type: Boolean,
default: false
}
},
methods: {
hasOneShowingChildren(children) {
// `hasOneShowingChildren` `children` `item.children`
const showingChildren = children.filter(item => {
// 使 JavaScript `filter` `children` `filter` `true` `hidden` `false` `showingChildren`
return!item.hidden
})
if (showingChildren.length === 1) {
// `showingChildren` `1` `1` `true`
return true
}
return false
// `showingChildren` `1` `false`使
}
}
}
</script>

@ -0,0 +1,89 @@
<template>
<scroll-bar>
<el-menu
mode="vertical"
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
background-color="#304156"
text-color="#bfcbd9"
active-text-color="#409EFF"
>
<sidebar-item :routes="routes"></sidebar-item>
</el-menu>
</scroll-bar>
</template>
<script>
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem'
import ScrollBar from '@/components/ScrollBar'
export default {
components: { SidebarItem, ScrollBar },
computed: {
...mapGetters([
'sidebar',
'routers'
]),
routes() {
// return this.$router.options.routes
return this.routers
},
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>
#abc
<template>
<!-- 使用名为 `scroll-bar` 的自定义组件可能是用于实现滚动条相关功能的组件比如自定义滚动条样式添加滚动交互逻辑等该组件作为外层容器包裹内部的 `el-menu` 组件以便为菜单提供滚动功能如果有相应实现的话 -->
<scroll-bar>
<!-- 使用 Element UI `el-menu` 菜单组件设置菜单模式为 `vertical`垂直方向展示`:show-timeout` 属性设置为 `200`表示菜单展开或收起时过渡动画的显示时长单位可能是毫秒`:default-active` 属性绑定为 `$route.path`意味着会根据当前路由的路径来自动设置默认激活的菜单项与当前页面匹配的菜单项处于激活状态`:collapse` 属性绑定 `isCollapse` 变量在计算属性中定义与侧边栏是否折叠相关用于控制菜单是否折叠显示同时设置了背景颜色文本颜色以及激活状态下的文本颜色用于定义菜单的整体外观颜色风格 -->
<el-menu
mode="vertical"
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
background-color="#304156"
text-color="#bfcbd9"
active-text-color="#409EFF"
>
<!-- 使用名为 `sidebar-item` 的自定义组件 `script` 部分有导入和注册用于根据传入的路由配置信息动态生成侧边栏菜单的具体内容通过 `:routes` 属性传递 `routes` 变量在计算属性中定义包含了菜单相关的路由配置数据用于递归地构建完整的侧边栏菜单结构展示各个菜单项及其嵌套子菜单等内容 -->
<sidebar-item :routes="routes"></sidebar-item>
</el-menu>
</scroll-bar>
</template>
<script>
// `vuex` `mapGetters` Vuex `getters` 便 Vuex 便使
import { mapGetters } from 'vuex';
// `SidebarItem`
import SidebarItem from './SidebarItem';
// `@/components/` `ScrollBar` `el-menu`
import ScrollBar from '@/components/ScrollBar';
export default {
components: {
// `components` `SidebarItem` `ScrollBar` 使 Vue.js 使
SidebarItem,
ScrollBar
},
computed: {
// 使 `mapGetters` Vuex `sidebar` `routers` `getters` `this.sidebar` `this.routers` `sidebar` `routers`
...mapGetters([
'sidebar',
'routers'
]),
routes() {
// `routes` `return this.$router.options.routes` `this.routers` `mapGetters` Vuex `sidebar-item`
return this.routers;
},
isCollapse() {
// `isCollapse` `this.sidebar.opened` `sidebar.opened` `false` `isCollapse` `true` `false` `el-menu` `:collapse`
return!this.sidebar.opened;
}
}
}
</script>

@ -0,0 +1,3 @@
export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar'
export { default as AppMain } from './AppMain'

@ -0,0 +1,41 @@
import store from '@/store'
const { body } = document
const WIDTH = 1024
const RATIO = 3
export default {
watch: {
$route(route) {
if (this.device === 'mobile' && this.sidebar.opened) {
store.dispatch('CloseSideBar', { withoutAnimation: false })
}
}
},
beforeMount() {
window.addEventListener('resize', this.resizeHandler)
},
mounted() {
const isMobile = this.isMobile()
if (isMobile) {
store.dispatch('ToggleDevice', 'mobile')
store.dispatch('CloseSideBar', { withoutAnimation: true })
}
},
methods: {
isMobile() {
const rect = body.getBoundingClientRect()
return rect.width - RATIO < WIDTH
},
resizeHandler() {
if (!document.hidden) {
const isMobile = this.isMobile()
store.dispatch('ToggleDevice', isMobile ? 'mobile' : 'desktop')
if (isMobile) {
store.dispatch('CloseSideBar', { withoutAnimation: true })
}
}
}
}
}

@ -0,0 +1,386 @@
<template>
<div>
<el-card class="login-form-layout">
<el-form autoComplete="on"
:model="loginForm"
:rules="loginRules"
ref="loginForm"
label-position="left">
<div style="text-align: center">
<svg-icon icon-class="login-mall" style="width: 56px;height: 56px;color: #409EFF"></svg-icon>
</div>
<h2 class="login-title color-main">mall-admin-web</h2>
<el-form-item prop="username">
<el-input name="username"
type="text"
v-model="loginForm.username"
autoComplete="on"
placeholder="请输入用户名">
<span slot="prefix">
<svg-icon icon-class="user" class="color-main"></svg-icon>
</span>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input name="password"
:type="pwdType"
@keyup.enter.native="handleLogin"
v-model="loginForm.password"
autoComplete="on"
placeholder="请输入密码">
<span slot="prefix">
<svg-icon icon-class="password" class="color-main"></svg-icon>
</span>
<span slot="suffix" @click="showPwd">
<svg-icon icon-class="eye" class="color-main"></svg-icon>
</span>
</el-input>
</el-form-item>
<el-form-item style="margin-bottom: 60px;text-align: center">
<el-button style="width: 45%" type="primary" :loading="loading" @click.native.prevent="handleLogin">
登录
</el-button>
<el-button style="width: 45%" type="primary" @click.native.prevent="handleTry">
获取体验账号
</el-button>
</el-form-item>
</el-form>
</el-card>
<img :src="login_center_bg" class="login-center-layout">
<el-dialog
title="公众号二维码"
:visible.sync="dialogVisible"
:show-close="false"
:center="true"
width="30%">
<div style="text-align: center">
<span class="font-title-large"><span class="color-main font-extra-large">关注公众号</span>回复<span class="color-main font-extra-large">体验</span>获取体验账号</span>
<br>
<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>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogConfirm"></el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {isvalidUsername} from '@/utils/validate';
import {setSupport,getSupport,setCookie,getCookie} from '@/utils/support';
import login_center_bg from '@/assets/images/login_center_bg.png'
export default {
name: 'login',
data() {
const validateUsername = (rule, value, callback) => {
if (!isvalidUsername(value)) {
callback(new Error('请输入正确的用户名'))
} else {
callback()
}
};
const validatePass = (rule, value, callback) => {
if (value.length < 3) {
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() {
this.loginForm.username = getCookie("username");
this.loginForm.password = getCookie("password");
if(this.loginForm.username === undefined||this.loginForm.username==null||this.loginForm.username===''){
this.loginForm.username = 'admin';
}
if(this.loginForm.password === undefined||this.loginForm.password==null){
this.loginForm.password = '';
}
},
methods: {
showPwd() {
if (this.pwdType === 'password') {
this.pwdType = ''
} else {
this.pwdType = 'password'
}
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
// let isSupport = getSupport();
// if(isSupport===undefined||isSupport==null){
// this.dialogVisible =true;
// return;
// }
this.loading = true;
this.$store.dispatch('Login', this.loginForm).then(() => {
this.loading = false;
setCookie("username",this.loginForm.username,15);
setCookie("password",this.loginForm.password,15);
this.$router.push({path: '/'})
}).catch(() => {
this.loading = false
})
} else {
console.log('参数验证不合法!');
return false
}
})
},
handleTry(){
this.dialogVisible =true
},
dialogConfirm(){
this.dialogVisible =false;
setSupport(true);
},
dialogCancel(){
this.dialogVisible = false;
setSupport(false);
}
}
}
</script>
<style scoped>
.login-form-layout {
position: absolute;
left: 0;
right: 0;
width: 360px;
margin: 140px auto;
border-top: 10px solid #409EFF;
}
.login-title {
text-align: center;
}
.login-center-layout {
background: #409EFF;
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
margin-top: 200px;
}
</style>
#abc
<template>
<div>
<!-- 使用 Element UI `el-card` 组件创建一个卡片式的布局容器类名为 `login-form-layout`用于包裹登录相关的表单元素使其在页面上呈现出特定的视觉效果和布局样式 -->
<el-card class="login-form-layout">
<!-- 使用 Element UI `el-form` 表单组件设置自动补全功能为 `on`绑定数据模型为 `loginForm`关联表单验证规则为 `loginRules`设置表单引用名为 `loginForm`标签位置为 `left`通常指表单标签在输入框左侧显示用于构建登录表单 -->
<el-form autoComplete="on"
:model="loginForm"
:rules="loginRules"
ref="loginForm"
label-position="left">
<!-- 一个 `div` 元素设置文本居中对齐内部使用 `svg-icon` 组件展示一个名为 `login-mall` 的图标用于在登录表单上方显示一个具有特定样式宽度高度颜色的图标可能起到品牌标识等作用 -->
<div style="text-align: center">
<svg-icon icon-class="login-mall" style="width: 56px;height: 56px;color: #409EFF"></svg-icon>
</div>
<!-- 一个 `h2` 标题元素类名为 `login-title` `color-main`用于显示页面标题mall-admin-web通过 `color-main` 类可能会应用特定的主色调样式 -->
<h2 class="login-title color-main">mall-admin-web</h2>
<!-- `el-form-item` 表单元素指定了验证属性 `prop` `username`用于包裹用户名输入框相关的内容对应表单中用户名的输入和验证部分 -->
<el-form-item prop="username">
<!-- 使用 Element UI `el-input` 输入框组件设置 `name` 属性为 `username`类型为 `text`双向绑定 `loginForm.username` 数据自动补全功能为 `on`设置占位提示文字为请输入用户名用于接收用户输入的用户名 -->
<el-input name="username"
type="text"
v-model="loginForm.username"
autoComplete="on"
placeholder="请输入用户名">
<!-- 使用 `slot` 插槽在输入框前缀位置插入一个 `svg-icon` 组件图标类名为 `user` 且应用 `color-main` 用于在用户名输入框前面展示一个表示用户的图标增强视觉提示效果 -->
<span slot="prefix">
<svg-icon icon-class="user" class="color-main"></svg-icon>
</span>
</el-input>
</el-form-item>
<!-- `el-form-item` 表单元素指定验证属性 `prop` `password`用于包裹密码输入框相关的内容对应表单中密码的输入和验证部分 -->
<el-form-item prop="password">
<!-- 使用 Element UI `el-input` 输入框组件设置 `name` 属性为 `password`类型通过 `pwdType` 变量动态控制用于实现密码显示隐藏切换功能监听回车键按下事件`@keyup.enter.native`并触发 `handleLogin` 方法双向绑定 `loginForm.password` 数据自动补全功能为 `on`设置占位提示文字为请输入密码用于接收用户输入的密码 -->
<el-input name="password"
:type="pwdType"
@keyup.enter.native="handleLogin"
v-model="loginForm.password"
autoComplete="on"
placeholder="请输入密码">
<!-- 使用 `slot` 插槽在输入框前缀位置插入一个 `svg-icon` 组件图标类名为 `password` 且应用 `color-main` 用于在密码输入框前面展示一个表示密码的图标增强视觉提示效果 -->
<span slot="prefix">
<svg-icon icon-class="password" class="color-main"></svg-icon>
</span>
<!-- 使用 `slot` 插槽在输入框后缀位置插入一个 `svg-icon` 组件图标类名为 `eye` 且应用 `color-main` 点击该图标可触发 `showPwd` 方法用于实现密码显示隐藏的切换功能通过点击眼睛图标来切换密码的可见性 -->
<span slot="suffix" @click="showPwd">
<svg-icon icon-class="eye" class="color-main"></svg-icon>
</span>
</el-input>
</el-form-item>
<!-- `el-form-item` 表单元素设置了底部外边距为 `60px` 且文本居中对齐用于放置登录和获取体验账号的按钮作为表单底部的操作按钮区域 -->
<el-form-item style="margin-bottom: 60px;text-align: center">
<!-- 使用 Element UI `el-button` 按钮组件设置样式宽度为 `45%`按钮类型为 `primary`通常表示主要的突出显示的按钮样式通过 `loading` 变量控制加载状态显示如按钮显示加载动画等点击时触发 `handleLogin` 方法阻止默认行为防止表单提交等默认操作用于执行登录操作 -->
<el-button style="width: 45%" type="primary" :loading="loading" @click.native.prevent="handleLogin">
登录
</el-button>
<!-- 使用 Element UI `el-button` 按钮组件设置样式宽度为 `45%`按钮类型为 `primary`点击时触发 `handleTry` 方法阻止默认行为用于执行获取体验账号的操作 -->
<el-button style="width: 45%" type="primary" @click.native.prevent="handleTry">
获取体验账号
</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 使用 `img` 图片元素通过 `:src` 绑定 `login_center_bg` 变量 `data` 中定义应该是图片资源路径类名为 `login-center-layout`用于展示登录页面中心位置的背景图片根据样式设置其布局和显示效果 -->
<img :src="login_center_bg" class="login-center-layout">
<!-- 使用 Element UI `el-dialog` 对话框组件设置标题为公众号二维码通过双向绑定控制对话框的显示隐藏状态`:visible.sync`设置不显示关闭按钮`:show-close="false"`使其居中显示`:center="true"`宽度为 `30%`用于弹出展示公众号二维码相关信息的对话框 -->
<el-dialog
title="公众号二维码"
:visible.sync="dialogVisible"
:show-close="false"
:center="true"
width="30%">
<!-- 一个 `div` 元素设置文本居中对齐内部包含文本和图片元素用于在对话框中展示引导用户关注公众号获取体验账号的相关提示信息以及公众号二维码图片 -->
<div style="text-align: center">
<span class="font-title-large"><span class="color-main font-extra-large">关注公众号</span>回复<span class="color-main font-extra-large">体验</span>获取体验账号</span>
<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">
</div>
<span slot="footer" class="dialog-footer">
<!-- 使用 Element UI `el-button` 按钮组件按钮类型为 `primary`点击时触发 `dialogConfirm` 方法用于在对话框中执行确定操作 -->
<el-button type="primary" @click="dialogConfirm"></el-button>
</span>
</el-dialog>
</div>
</template>
<script>
// `@/utils/validate` `isvalidUsername`
import {isvalidUsername} from '@/utils/validate';
// `@/utils/support` `setSupport` `getSupport` `setCookie` `getCookie` `Cookie`
import {setSupport,getSupport,setCookie,getCookie} from '@/utils/support';
// `login_center_bg` `@/assets/images/login_center_bg.png`
import login_center_bg from '@/assets/images/login_center_bg.png'
export default {
name: 'login',
data() {
const validateUsername = (rule, value, callback) => {
// `isvalidUsername` `Error`
if (!isvalidUsername(value)) {
callback(new Error('请输入正确的用户名'))
} else {
callback()
}
};
const validatePass = (rule, value, callback) => {
// `3` `Error` 3
if (value.length < 3) {
callback(new Error('密码不能小于3位'))
} else {
callback()
}
};
return {
// `username` `password` `Cookie`
loginForm: {
username: '',
password: '',
},
// `username` `required: true``trigger: 'blur'`使 `validateUsername` `password` 使 `validatePass`
loginRules: {
username: [{required: true, trigger: 'blur', validator: validateUsername}],
password: [{required: true, trigger: 'blur', validator: validatePass}]
},
// `false` `true` `false`
loading: false,
// `password` `showPwd` `password`
pwdType: 'password',
//
login_center_bg,
// `false` `true`
dialogVisible:false,
// 使 `false`
supportDialogVisible:false
}
},
created() {
// `Cookie` `loginForm.username` `loginForm.password` `null` `admin` `null` `Cookie`
this.loginForm.username = getCookie("username");
this.loginForm.password = getCookie("password");
if(this.loginForm.username === undefined||this.loginForm.username==null||this.loginForm.username===''){
this.loginForm.username = 'admin';
}
if(this.loginForm.password === undefined||this.loginForm.password==null){
this.loginForm.password = '';
}
},
methods: {
showPwd() {
// `pwdType` `password` 使 `pwdType` `password`
if (this.pwdType === 'password') {
this.pwdType = ''
} else {
this.pwdType = 'password'
}
},
handleLogin() {
// `$refs.loginForm` `validate` `valid` `true`
//
// `loading` `true` `$store.dispatch` `Login` Vuex `loading` `false`使 `setCookie` `Cookie` `15` `setCookie` `$router.push` `/``valid` `false` `false`
this.$refs.loginForm.validate(valid => {
if (valid) {
// let isSupport = getSupport();
// if(isSupport===undefined||isSupport==null){
// this.dialogVisible =true;
// return;
// }
this.loading = true;
this.$store.dispatch('Login', this.loginForm).then(() => {
this.loading = false;
setCookie("username",this.loginForm.username,15);
setCookie("password",this.loginForm.password,15);
this.$router.push({path: '/'})
}).catch(() => {
this.loading = false
})
} else {
console.log('参数验证不合法!');
return false
}
})
},
handleTry() {
// `dialogVisible` `true`
this.dialogVisible =true
},
dialogConfirm() {
// `dialogVisible` `false` `setSupport` `true` `setSupport`
this.dialogVisible =false;
setSupport(true);
},
dialogCancel() {
// `dialogVisible` `false` `setSupport` `false`
this.dialogVisible = false;
setSupport(false);
}
}
}
</script>
<style scoped>
// `login-form-layout` `absolute`使 `body` `0` `360px` `140px` `auto`

16146
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,74 @@
{
"name": "mall-admin-web",
"version": "1.0.0",
"description": "mall后台管理前端",
"author": "macro",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
},
"dependencies": {
"axios": "^0.18.0",
"echarts": "^4.2.0-rc.2",
"element-ui": "^2.3.7",
"js-cookie": "^2.2.0",
"normalize.css": "^8.0.0",
"nprogress": "^0.2.0",
"sass": "^1.32.8",
"v-charts": "^1.19.0",
"v-distpicker": "^1.0.20",
"vue": "^2.7.2",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"sass-loader": "^7.0.1",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"svg-sprite-loader": "^3.7.3",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

@ -0,0 +1,34 @@
<template> 
<brand-detail :is-edit='false'></brand-detail>
</template>
<script>
import BrandDetail from './components/BrandDetail'
export default {
name: 'addBrand',
components: { BrandDetail }
}
</script>
<style>
</style>
#abc
<template>
<!-- 使用BrandDetail组件并传递一个名为is-edit的属性值设置为false用于表示当前处于非编辑状态 -->
<brand-detail :is-edit='false'></brand-detail>
</template>
<script>
// BrandDetail./components/BrandDetailVue
import BrandDetail from './components/BrandDetail'
export default {
name: 'addBrand',
// BrandDetail使使
components: { BrandDetail }
}
</script>
<style>
/* 这里可以添加针对当前组件的样式规则,目前为空 */
</style>

@ -0,0 +1,145 @@
<template> 
<el-card class="form-container" shadow="never">
<el-form :model="brand" :rules="rules" ref="brandFrom" label-width="150px">
<el-form-item label="品牌名称:" prop="name">
<el-input v-model="brand.name"></el-input>
</el-form-item>
<el-form-item label="品牌首字母:">
<el-input v-model="brand.firstLetter"></el-input>
</el-form-item>
<el-form-item label="品牌LOGO" prop="logo">
<single-upload v-model="brand.logo"></single-upload>
</el-form-item>
<el-form-item label="品牌专区大图:">
<single-upload v-model="brand.bigPic"></single-upload>
</el-form-item>
<el-form-item label="品牌故事:">
<el-input
placeholder="请输入内容"
type="textarea"
v-model="brand.brandStory"
:autosize="true"></el-input>
</el-form-item>
<el-form-item label="排序:" prop="sort">
<el-input v-model.number="brand.sort"></el-input>
</el-form-item>
<el-form-item label="是否显示:">
<el-radio-group v-model="brand.showStatus">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="品牌制造商:">
<el-radio-group v-model="brand.factoryStatus">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('brandFrom')"></el-button>
<el-button v-if="!isEdit" @click="resetForm('brandFrom')"></el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
import {createBrand, getBrand, updateBrand} from '@/api/brand'
import SingleUpload from '@/components/Upload/singleUpload'
const defaultBrand={
bigPic: '',
brandStory: '',
factoryStatus: 0,
firstLetter: '',
logo: '',
name: '',
showStatus: 0,
sort: 0
};
export default {
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) {
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(response => {
this.$refs[formName].resetFields();
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 {
this.$message({
message: '验证失败',
type: 'error',
duration:1000
});
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.brand = Object.assign({},defaultBrand);
}
}
}
</script>
<style>
</style>

@ -0,0 +1,674 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float: right"
@click="searchBrandList()"
type="primary"
size="small">
查询结果
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="输入搜索:">
<el-input style="width: 203px" v-model="listQuery.keyword" placeholder="品牌名称/关键字"></el-input>
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button
class="btn-add"
@click="addBrand()"
size="mini">
添加
</el-button>
</el-card>
<div class="table-container">
<el-table ref="brandTable"
: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="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="品牌名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="品牌首字母" width="100" align="center">
<template slot-scope="scope">{{scope.row.firstLetter}}</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="品牌制造商" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleFactoryStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.factoryStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleShowStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.showStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column label="相关" width="220" align="center">
<template slot-scope="scope">
<span>商品</span>
<el-button
size="mini"
type="text"
@click="getProductList(scope.$index, scope.row)">100
</el-button>
<span>评价</span>
<el-button
size="mini"
type="text"
@click="getProductCommentList(scope.$index, scope.row)">1000
</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
: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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import {fetchList, updateShowStatus, updateFactoryStatus, deleteBrand} from '@/api/brand'
export default {
name: 'brandList',
data() {
return {
operates: [
{
label: "显示品牌",
value: "showBrand"
},
{
label: "隐藏品牌",
value: "hideBrand"
}
],
operateType: null,
listQuery: {
keyword: null,
pageNum: 1,
pageSize: 10
},
list: null,
total: null,
listLoading: true,
multipleSelection: []
}
},
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;
this.totalPage = response.data.totalPage;
this.pageSize = response.data.pageSize;
});
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleUpdate(index, row) {
this.$router.push({path: '/pms/updateBrand', query: {id: row.id}})
},
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();
},
handleBatchOperate() {
console.log(this.multipleSelection);
if (this.multipleSelection < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
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>
<style rel="stylesheet/scss" lang="scss" scoped>
</style>
#abc
<template>
<!-- 整个页面的最外层容器用于布局和包裹内部的各个组件与元素 -->
<div class="app-container">
<!-- 筛选搜索区域的卡片组件设置阴影效果为无 -->
<el-card class="filter-container" shadow="never">
<div>
<!-- 使用Element UI的图标组件展示搜索图标 -->
<i class="el-icon-search"></i>
<!-- 显示筛选搜索的文字说明 -->
<span>筛选搜索</span>
<!-- Element UI的按钮组件点击时触发searchBrandList方法样式设置为靠右类型为主要按钮尺寸为小 -->
<el-button
style="float: right"
@click="searchBrandList()"
type="primary"
size="small">
查询结果
</el-button>
</div>
<div style="margin-top: 15px">
<!-- 内联表单组件绑定数据模型为listQuery尺寸为小标签宽度为140px -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单项标签显示为输入搜索 -->
<el-form-item label="输入搜索:">
<!-- 输入框组件绑定数据模型为listQuery的keyword属性宽度为203px有占位提示文字 -->
<el-input style="width: 203px" v-model="listQuery.keyword" placeholder="品牌名称/关键字"></el-input>
</el-form-item>
</el-form>
</div>
</el-card>
<!-- 操作区域的卡片组件设置阴影效果为无 -->
<el-card class="operate-container" shadow="never">
<!-- 使用Element UI的图标组件展示票务图标此处可能表意不太准确需根据实际情况确认合适图标含义 -->
<i class="el-icon-tickets"></i>
<!-- 显示数据列表的文字说明 -->
<span>数据列表</span>
<!-- Element UI的按钮组件点击时触发addBrand方法尺寸为迷你 -->
<el-button
class="btn-add"
@click="addBrand()"
size="mini">
添加
</el-button>
</el-card>
<!-- 表格容器用于放置展示数据的表格 -->
<div class="table-container">
<!-- Element UI的表格组件绑定数据为list宽度为100%监听选择行变化事件加载状态变化设置边框 -->
<el-table ref="brandTable"
:data="list"
style="width: 100%"
@selection-change="handleSelectionChange"
v-loading="listLoading"
border>
<!-- 表格列类型为选择框宽度为60px内容居中对齐 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 表格列标签显示为编号宽度为100px内容居中对齐通过插槽作用域展示对应行的数据中的id属性 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 表格列标签显示为品牌名称内容居中对齐通过插槽作用域展示对应行的数据中的name属性 -->
<el-table-column label="品牌名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 表格列标签显示为品牌首字母宽度为100px内容居中对齐通过插槽作用域展示对应行的数据中的firstLetter属性 -->
<el-table-column label="品牌首字母" width="100" align="center">
<template slot-scope="scope">{{scope.row.firstLetter}}</template>
</el-table-column>
<!-- 表格列标签显示为排序宽度为100px内容居中对齐通过插槽作用域展示对应行的数据中的sort属性 -->
<el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<!-- 表格列标签显示为品牌制造商宽度为100px内容居中对齐内部包含一个开关组件用于切换工厂状态绑定对应行的数据中的factoryStatus属性并监听状态改变事件 -->
<el-table-column label="品牌制造商" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleFactoryStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.factoryStatus">
</el-switch>
</template>
</el-table-column>
<!-- 表格列标签显示为是否显示宽度为100px内容居中对齐内部包含一个开关组件用于切换显示状态绑定对应行的数据中的showStatus属性并监听状态改变事件 -->
<el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleShowStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.showStatus">
</el-switch>
</template>
</el-table-column>
<!-- 表格列标签显示为相关宽度为220px内容居中对齐内部包含查看商品评价相关的按钮点击可触发对应的方法并传递对应行数据 -->
<el-table-column label="相关" width="220" align="center">
<template slot-scope="scope">
<span>商品</span>
<el-button
size="mini"
type="text"
@click="getProductList(scope.$index, scope.row)">100
</el-button>
<span>评价</span>
<el-button
size="mini"
type="text"
@click="getProductCommentList(scope.$index, scope.row)">1000
</el-button>
</template>
</el-table-column>
<!-- 表格列标签显示为操作宽度为200px内容居中对齐内部包含编辑删除按钮点击可触发对应的操作方法并传递对应行数据 -->
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 批量操作区域的容器 -->
<div class="batch-operate-container">
<!-- 下拉选择框组件绑定数据模型为operateType有占位提示文字 -->
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<!-- 通过循环生成下拉选项每个选项的标签和值根据operates数组中的元素来确定 -->
<el-option
v-for="item in operates"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<!-- Element UI的按钮组件点击时触发handleBatchOperate方法样式设置有一定间隔类型为主要按钮尺寸为小 -->
<el-button
style="margin-left: 20px"
class="search-button"
@click="handleBatchOperate()"
type="primary"
size="small">
确定
</el-button>
</div>
<!-- 分页容器用于放置分页组件 -->
<div class="pagination-container">
<!-- Element UI的分页组件设置背景色监听页面尺寸和当前页变化事件布局包含多种分页相关元素绑定相关数据属性 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
// @/api/brand
import {fetchList, updateShowStatus, updateFactoryStatus, deleteBrand} from '@/api/brand'
export default {
name: 'brandList',
data() {
return {
//
operates: [
{
label: "显示品牌",
value: "showBrand"
},
{
label: "隐藏品牌",
value: "hideBrand"
}
],
// null
operateType: null,
//
listQuery: {
keyword: null,
pageNum: 1,
pageSize: 10
},
// null
list: null,
// null
total: null,
// true
listLoading: true,
//
multipleSelection: []
}
},
created() {
// getList
this.getList();
},
methods: {
getList() {
// true
this.listLoading = true;
// fetchList
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
this.totalPage = response.data.totalPage;
this.pageSize = response.data.pageSize;
});
},
handleSelectionChange(val) {
// multipleSelection
this.multipleSelection = val;
},
handleUpdate(index, row) {
// id
this.$router.push({path: '/pms/updateBrand', query: {id: row.id}})
},
handleDelete(index, row) {
// deleteBrand
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) {
// URLSearchParamsidupdateFactoryStatus
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) {
// URLSearchParamsidupdateShowStatus
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) {
// 1
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
//
this.listQuery.pageNum = val;
this.getList();
},
searchBrandList() {
// 1
this.listQuery.pageNum = 1;
this.getList();
},
handleBatchOperate() {
console.log(this.multipleSelection);
//
if (this.multipleSelection.length < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
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 = [];
// idids
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
updateShowStatus(data).then(response => {
this.getList();
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
addBrand() {
//
this.$router.push({path: '/pms/addBrand'})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
/*
scoped属性表示样式只作用于当前组件内部的元素避免样式污染其他组件 */
</

@ -0,0 +1,37 @@
<template> 
<brand-detail :is-edit='true'></brand-detail>
</template>
<script>
import BrandDetail from './components/BrandDetail'
export default {
name: 'updateBrand',
components: { BrandDetail }
}
</script>
<style>
</style>
#abc
<template>
<!-- 使用BrandDetail组件并传递一个名为is-edit的属性值设置为true意味着当前处于编辑状态
可能用于控制BrandDetail组件内部呈现出可编辑的界面等相关功能 -->
<brand-detail :is-edit='true'></brand-detail>
</template>
<script>
// './components/BrandDetail'BrandDetailVue
// 使
import BrandDetail from './components/BrandDetail'
export default {
name: 'updateBrand',
// BrandDetail使template使
//
components: { BrandDetail }
}
</script>
<style>
/* CSS
比如对组件内元素的布局颜色字体等样式相关的设置 */
</style>

@ -0,0 +1,30 @@
<template> 
<product-detail :is-edit='false'></product-detail>
</template>
<script>
import ProductDetail from './components/ProductDetail'
export default {
name: 'addProduct',
components: { ProductDetail }
}
</script>
<style>
</style>
#abc
<template>
<!-- 这里使用了名为 `product-detail` 的自定义组件通过 `:is-edit='false'` 属性绑定 `product-detail` 组件传递了一个名为 `is-edit` 的属性值为 `false`意味着当前是新增商品的操作模式而非编辑商品模式`product-detail` 组件应该会根据这个属性来展示相应的界面和执行对应的逻辑 -->
<product-detail :is-edit='false'></product-detail>
</template>
<script>
// `./components/` `ProductDetail`
import ProductDetail from './components/ProductDetail';
export default {
name: 'addProduct',
// `components` `ProductDetail` 使`addProduct`使 `<product-detail>` Vue.js
components: { ProductDetail }
}
</script>
<style>
/* 这里的样式部分目前为空,可根据 `addProduct` 组件整体的视觉设计需求,添加相应的 CSS 样式规则,比如设置组件的布局、大小、颜色等样式属性,使其在页面中呈现出符合预期的外观效果 */
</style>

@ -0,0 +1,952 @@
<template>
<div style="margin-top: 50px">
<el-form :model="value" ref="productAttrForm" label-width="120px" class="form-inner-container" size="small">
<el-form-item label="属性类型:">
<el-select v-model="value.productAttributeCategoryId"
placeholder="请选择属性类型"
@change="handleProductAttrChange">
<el-option
v-for="item in productAttributeCategoryOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商品规格:">
<el-card shadow="never" class="cardBg">
<div v-for="(productAttr,idx) in selectProductAttr">
{{productAttr.name}}
<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>
<div v-else>
<el-checkbox-group v-model="selectProductAttr[idx].values">
<div v-for="(item,index) in selectProductAttr[idx].options" style="display: inline-block"
class="littleMarginLeft">
<el-checkbox :label="item" :key="item"></el-checkbox>
<el-button type="text" class="littleMarginLeft" @click="handleRemoveProductAttrValue(idx,index)">
</el-button>
</div>
</el-checkbox-group>
<el-input v-model="addProductAttrValue" style="width: 160px;margin-left: 10px" clearable></el-input>
<el-button class="littleMarginLeft" @click="handleAddProductAttrValue(idx)"></el-button>
</div>
</div>
</el-card>
<el-table style="width: 100%;margin-top: 20px"
:data="value.skuStockList"
border>
<el-table-column
v-for="(item,index) in selectProductAttr"
: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="100"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.price"></el-input>
</template>
</el-table-column>
<el-table-column
label="促销价格"
width="100"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.promotionPrice"></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="80"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.lowStock"></el-input>
</template>
</el-table-column>
<el-table-column
label="SKU编号"
width="160"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.skuCode"></el-input>
</template>
</el-table-column>
<el-table-column
label="操作"
width="80"
align="center">
<template slot-scope="scope">
<el-button
type="text"
@click="handleRemoveProductSku(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-button
type="primary"
style="margin-top: 20px"
@click="handleRefreshProductSkuList">刷新列表
</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 label="商品相册:">
<multi-upload v-model="selectProductPics"></multi-upload>
</el-form-item>
<el-form-item label="商品详情:">
<el-tabs v-model="activeHtmlName" type="card">
<el-tab-pane label="电脑端详情" name="pc">
<tinymce :width="595" :height="300" v-model="value.detailHtml"></tinymce>
</el-tab-pane>
<el-tab-pane label="移动端详情" name="mobile">
<tinymce :width="595" :height="300" v-model="value.detailMobileHtml"></tinymce>
</el-tab-pane>
</el-tabs>
</el-form-item>
<el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleNext"></el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {fetchList as fetchProductAttrCateList} from '@/api/productAttrCate'
import {fetchList as fetchProductAttrList} from '@/api/productAttr'
import SingleUpload from '@/components/Upload/singleUpload'
import MultiUpload from '@/components/Upload/multiUpload'
import Tinymce from '@/components/Tinymce'
export default {
name: "ProductAttrDetail",
components: {SingleUpload, MultiUpload, Tinymce},
props: {
value: Object,
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
//
hasEditCreated:false,
//
productAttributeCategoryOptions: [],
//
selectProductAttr: [],
//
selectProductParam: [],
//
selectProductAttrPics: [],
//
addProductAttrValue: '',
//
activeHtmlName: 'pc'
}
},
computed: {
//
hasAttrPic() {
if (this.selectProductAttrPics.length < 1) {
return false;
}
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() {
this.getProductAttrCateList();
},
watch: {
productId:function (newValue) {
if(!this.isEdit)return;
if(this.hasEditCreated)return;
if(newValue===undefined||newValue==null||newValue===0)return;
this.handleEditCreated();
}
},
methods: {
handleEditCreated() {
//id
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>
<style scoped>
.littleMarginLeft {
margin-left: 10px;
}
.littleMarginTop {
margin-top: 10px;
}
.paramInput {
width: 250px;
}
.paramInputLabel {
display: inline-block;
width: 100px;
text-align: right;
padding-right: 10px
}
.cardBg {
background: #F8F9FC;
}
</style>
#abc
<template>
<!-- 整体的外层 div设置了距离顶部 50px margin用于页面布局上的间隔 -->
<div style="margin-top: 50px">
<!-- el-form 表单组件绑定数据模型为 value设置了表单引用名标签宽度类名以及尺寸为小用于收集和展示商品相关信息 -->
<el-form :model="value" ref="productAttrForm" label-width="120px" class="form-inner-container" size="small">
<!-- 表单项标签显示为属性类型 -->
<el-form-item label="属性类型:">
<!-- el-select 下拉选择框组件绑定 v-model value.productAttributeCategoryId有占位提示文字并且监听 change 事件 -->
<el-select v-model="value.productAttributeCategoryId"
placeholder="请选择属性类型"
@change="handleProductAttrChange">
<!-- 通过循环生成下拉选项每个选项的标签和值根据 productAttributeCategoryOptions 数组中的元素来确定 -->
<el-option
v-for="item in productAttributeCategoryOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 表单项标签显示为商品规格用于展示和操作商品规格相关内容 -->
<el-form-item label="商品规格:">
<!-- el-card 卡片组件设置阴影效果为无添加了自定义类名 cardBg用于对商品规格部分进行布局划分 -->
<el-card shadow="never" class="cardBg">
<!-- 循环遍历 selectProductAttr 数组展示每个商品属性相关内容 -->
<div v-for="(productAttr,idx) in selectProductAttr">
<!-- 显示商品属性的名称 -->
{{productAttr.name}}
<!-- 根据商品属性的 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>
<!-- handAddStatus 不为 0 展示可动态操作的复选框组包含添加删除选项等功能 -->
<div v-else>
<el-checkbox-group v-model="selectProductAttr[idx].values">
<div v-for="(item,index) in selectProductAttr[idx].options" style="display: inline-block"
class="littleMarginLeft">
<el-checkbox :label="item" :key="item"></el-checkbox>
<el-button type="text" class="littleMarginLeft" @click="handleRemoveProductAttrValue(idx,index)">
</el-button>
</div>
</el-checkbox-group>
<el-input v-model="addProductAttrValue" style="width: 160px;margin-left: 10px" clearable></el-input>
<el-button class="littleMarginLeft" @click="handleAddProductAttrValue(idx)"></el-button>
</div>
</div>
</el-card>
<!-- el-table 表格组件设置宽度为 100%绑定数据为 value.skuStockList设置边框用于展示商品 SKU 相关信息 -->
<el-table style="width: 100%;margin-top: 20px"
:data="value.skuStockList"
border>
<!-- 循环生成表格列根据 selectProductAttr 数组中的每个商品属性生成对应列用于展示每个 SKU 对应的属性值 -->
<el-table-column
v-for="(item,index) in selectProductAttr"
:label="item.name"
:key="item.id"
align="center">
<template slot-scope="scope">
{{getProductSkuSp(scope.row,index)}}
</template>
</el-table-column>
<!-- 表格列标签显示为销售价格宽度为 100px内容居中对齐内部使用输入框展示和编辑价格 -->
<el-table-column
label="销售价格"
width="100"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.price"></el-input>
</template>
</el-table-column>
<!-- 表格列标签显示为促销价格宽度为 100px内容居中对齐内部使用输入框展示和编辑促销价格 -->
<el-table-column
label="促销价格"
width="100"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.promotionPrice"></el-input>
</template>
</el-table-column>
<!-- 表格列标签显示为商品库存宽度为 80px内容居中对齐内部使用输入框展示和编辑库存数量 -->
<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>
<!-- 表格列标签显示为库存预警值宽度为 80px内容居中对齐内部使用输入框展示和编辑库存预警值 -->
<el-table-column
label="库存预警值"
width="80"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.lowStock"></el-input>
</template>
</el-table-column>
<!-- 表格列标签显示为SKU 编号宽度为 160px内容居中对齐内部使用输入框展示和编辑 SKU 编号 -->
<el-table-column
label="SKU 编号"
width="160"
align="center">
<template slot-scope="scope">
<el-input v-model="scope.row.skuCode"></el-input>
</template>
</el-table-column>
<!-- 表格列标签显示为操作宽度为 80px内容居中对齐内部包含删除按钮点击可删除对应行的 SKU 数据 -->
<el-table-column
label="操作"
width="80"
align="center">
<template slot-scope="scope">
<el-button
type="text"
@click="handleRemoveProductSku(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- el-button 按钮组件类型为主要按钮点击时触发 handleRefreshProductSkuList 方法用于刷新商品 SKU 列表 -->
<el-button
type="primary"
style="margin-top: 20px"
@click="handleRefreshProductSkuList">刷新列表
</el-button>
<!-- el-button 按钮组件类型为主要按钮点击时触发 handleSyncProductSkuPrice 方法用于同步商品 SKU 的价格 -->
<el-button
type="primary"
style="margin-top: 20px"
@click="handleSyncProductSkuPrice">同步价格
</el-button>
<!-- el-button 按钮组件类型为主要按钮点击时触发 handleSyncProductSkuStock 方法用于同步商品 SKU 的库存 -->
<el-button
type="primary"
style="margin-top: 20px"
@click="handleSyncProductSkuStock">同步库存
</el-button>
</el-form-item>
<!-- 根据 hasAttrPic 计算属性判断是否显示商品属性图片相关内容只有当有属性图片时才展示 -->
<el-form-item label="属性图片:" v-if="hasAttrPic">
<!-- el-card 卡片组件设置阴影效果为无添加了自定义类名 cardBg用于对商品属性图片部分进行布局划分 -->
<el-card shadow="never" class="cardBg">
<!-- 循环遍历 selectProductAttrPics 数组展示每个商品属性图片相关内容使用 single-upload 组件上传图片 -->
<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 卡片组件设置阴影效果为无添加了自定义类名 cardBg用于对商品参数部分进行布局划分 -->
<el-card shadow="never" class="cardBg">
<!-- 循环遍历 selectProductParam 数组展示每个商品参数相关内容根据输入类型选择不同的输入组件展示和编辑参数值 -->
<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>
<!-- 表单项标签显示为商品相册使用 multi-upload 组件用于上传商品的相册图片 -->
<el-form-item label="商品相册:">
<multi-upload v-model="selectProductPics"></multi-upload>
</el-form-item>
<!-- 表单项标签显示为商品详情使用 el-tabs 选项卡组件展示电脑端和移动端的商品详情分别使用 tinymce 富文本编辑器进行编辑 -->
<el-form-item label="商品详情:">
<el-tabs v-model="activeHtmlName" type="card">
<el-tab-pane label="电脑端详情" name="pc">
<tinymce :width="595" :height="300" v-model="value.detailHtml"></tinymce>
</el-tab-pane>
<el-tab-pane label="移动端详情" name="mobile">
<tinymce :width="595" :height="300" v-model="value.detailMobileHtml"></tinymce>
</el-tab-pane>
</el-tabs>
</el-form-item>
<!-- 表单项用于放置操作按钮设置文本居中对齐包含上一步下一步按钮点击分别触发对应的方法 -->
<el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleNext"></el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
// @/api/productAttrCate fetchList fetchProductAttrCateList
import {fetchList as fetchProductAttrCateList} from '@/api/productAttrCate'
// @/api/productAttr fetchList fetchProductAttrList
import {fetchList as fetchProductAttrList} from '@/api/productAttr'
// SingleUpload
import SingleUpload from '@/api/SingleUpload'
// MultiUpload
import MultiUpload from '@/api/MultiUpload'
// Tinymce
import Tinymce from '@/api/Tinymce'
export default
name: "ProductAttrDetail",
components: {SingleUpload, MultiUpload, Tinymce},
props: {
// value
value: Object,
// isEdit false
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
// false
hasEditCreated: false,
//
productAttributeCategoryOptions: [],
//
selectProductAttr: [],
//
selectProductParam: [],
//
selectProductAttrPics: [],
//
addProductAttrValue: '',
// 'pc'
activeHtmlName: 'pc'
}
},
computed: {
// selectProductAttrPics
hasAttrPic() {
if (this.selectProductAttrPics.length < 1) {
return false;
}
return true;
},
// value
productId() {
return this.value.id;
},
// value
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() {
// getProductAttrCateList
this.getProductAttrCateList();
},
watch: {
productId: function (newValue) {
//
if (!this.isEdit) return;
//
if (this.hasEditCreated) return;
// undefinednull 0
if (newValue === undefined || newValue == null || newValue === 0) return;
// handleEditCreated
this.handleEditCreated();
}
},
methods:
handleEditCreated() {
// id id
if (this.value.productAttributeCategoryId!= null) {
this.handleProductAttrChange(this.value.productAttributeCategoryId);
}
// true
this.hasEditCreated = true;
},
getProductAttrCateList()

@ -0,0 +1,396 @@
<template> 
<el-card class="form-container" shadow="never">
<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-steps>
<product-info-detail
v-show="showStatus[0]"
v-model="productParam"
:is-edit="isEdit"
@nextStep="nextStep">
</product-info-detail>
<product-sale-detail
v-show="showStatus[1]"
v-model="productParam"
:is-edit="isEdit"
@nextStep="nextStep"
@prevStep="prevStep">
</product-sale-detail>
<product-attr-detail
v-show="showStatus[2]"
v-model="productParam"
:is-edit="isEdit"
@nextStep="nextStep"
@prevStep="prevStep">
</product-attr-detail>
<product-relation-detail
v-show="showStatus[3]"
v-model="productParam"
:is-edit="isEdit"
@prevStep="prevStep"
@finishCommit="finishCommit">
</product-relation-detail>
</el-card>
</template>
<script>
import ProductInfoDetail from './ProductInfoDetail';
import ProductSaleDetail from './ProductSaleDetail';
import ProductAttrDetail from './ProductAttrDetail';
import ProductRelationDetail from './ProductRelationDetail';
import {createProduct,getProduct,updateProduct} from '@/api/product';
const defaultProductParam = {
albumPics: '',
brandId: null,
brandName: '',
deleteStatus: 0,
description: '',
detailDesc: '',
detailHtml: '',
detailMobileHtml: '',
detailTitle: '',
feightTemplateId: 0,
flashPromotionCount: 0,
flashPromotionId: 0,
flashPromotionPrice: 0,
flashPromotionSort: 0,
giftPoint: 0,
giftGrowth: 0,
keywords: '',
lowStock: 0,
name: '',
newStatus: 0,
note: '',
originalPrice: 0,
pic: '',
//{memberLevelId: 0,memberPrice: 0,memberLevelName: null}
memberPriceList: [],
//
productFullReductionList: [{fullPrice: 0, reducePrice: 0}],
//
productLadderList: [{count: 0,discount: 0,price: 0}],
previewStatus: 0,
price: 0,
productAttributeCategoryId: null,
//{productAttributeId: 0, value: ''}
productAttributeValueList: [],
//sku{lowStock: 0, pic: '', price: 0, sale: 0, skuCode: '', spData: '', stock: 0}
skuStockList: [],
//{subjectId: 0}
subjectProductRelationList: [],
//{prefrenceAreaId: 0}
prefrenceAreaProductRelationList: [],
productCategoryId: null,
productCategoryName: '',
productSn: '',
promotionEndTime: '',
promotionPerLimit: 0,
promotionPrice: null,
promotionStartTime: '',
promotionType: 0,
publishStatus: 0,
recommandStatus: 0,
sale: 0,
serviceIds: '',
sort: 0,
stock: 0,
subTitle: '',
unit: '',
usePointLimit: 0,
verifyStatus: 0,
weight: 0
};
export default {
name: 'ProductDetail',
components: {ProductInfoDetail, ProductSaleDetail, ProductAttrDetail, ProductRelationDetail},
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
active: 0,
productParam: Object.assign({}, defaultProductParam),
showStatus: [true, false, false, false]
}
},
created(){
if(this.isEdit){
getProduct(this.$route.query.id).then(response=>{
this.productParam=response.data;
});
}
},
methods: {
hideAll() {
for (let i = 0; i < this.showStatus.length; i++) {
this.showStatus[i] = false;
}
},
prevStep() {
if (this.active > 0 && this.active < this.showStatus.length) {
this.active--;
this.hideAll();
this.showStatus[this.active] = true;
}
},
nextStep() {
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{
createProduct(this.productParam).then(response=>{
this.$message({
type: 'success',
message: '提交成功',
duration:1000
});
location.reload();
});
}
})
}
}
}
</script>
<style>
.form-container {
width: 960px;
}
.form-inner-container {
width: 800px;
}
</style>
#abc
<template>
<!-- 使用Element UI的el-card组件创建一个卡片式容器设置类名为form-container阴影效果为无用于对内部内容进行布局包裹 -->
<el-card class="form-container" shadow="never">
<!-- 使用Element UI的el-steps步骤条组件绑定:active属性用于控制当前激活的步骤设置完成状态的样式为success并且让步骤标题居中对齐 -->
<el-steps :active="active" finish-status="success" align-center>
<!-- 定义一个步骤标题为填写商品信息具体内容由对应的组件product-info-detail来填充 -->
<el-step title="填写商品信息"></el-step>
<!-- 定义一个步骤标题为填写商品促销具体内容由对应的组件product-sale-detail来填充 -->
<el-step title="填写商品促销"></el-step>
<!-- 定义一个步骤标题为填写商品属性具体内容由对应的组件product-attr-detail来填充 -->
<el-step title="填写商品属性"></el-step>
<!-- 定义一个步骤标题为选择商品关联具体内容由对应的组件product-relation-detail来填充 -->
<el-step title="选择商品关联"></el-step>
</el-steps>
<!-- 使用product-info-detail组件通过v-show指令根据showStatus数组中对应索引的值来控制显示隐藏绑定v-model用于双向数据绑定传递is-edit属性并且监听nextStep事件 -->
<product-info-detail
v-show="showStatus[0]"
v-model="productParam"
:is-edit="isEdit"
@nextStep="nextStep">
</product-info-detail>
<!-- 使用product-sale-detail组件通过v-show指令根据showStatus数组中对应索引的值来控制显示隐藏绑定v-model用于双向数据绑定传递is-edit属性并且监听nextStep和prevStep事件 -->
<product-sale-detail
v-show="showStatus[1]"
v-model="productParam"
:is-edit="isEdit"
@nextStep="nextStep"
@prevStep="prevStep">
</product-sale-detail>
<!-- 使用product-attr-detail组件通过v-show指令根据showStatus数组中对应索引的值来控制显示隐藏绑定v-model用于双向数据绑定传递is-edit属性并且监听nextStep和prevStep事件 -->
<product-attr-detail
v-show="showStatus[2]"
v-model="productParam"
:is-edit="isEdit"
@nextStep="nextStep"
@prevStep="prevStep">
</product-attr-detail>
<!-- 使用product-relation-detail组件通过v-show指令根据showStatus数组中对应索引的值来控制显示隐藏绑定v-model用于双向数据绑定传递is-edit属性并且监听prevStep和finishCommit事件 -->
<product-relation-detail
v-show="showStatus[3]"
v-model="productParam"
:is-edit="isEdit"
@prevStep="prevStep"
@finishCommit="finishCommit">
</product-relation-detail>
</el-card>
</template>
<script>
// ProductInfoDetailVue
import ProductInfoDetail from './ProductInfoDetail';
// ProductSaleDetailVue
import ProductSaleDetail from './ProductSaleDetail';
// ProductAttrDetailVue
import ProductAttrDetail from './ProductAttrDetail';
// ProductRelationDetailVue
import ProductRelationDetail from './ProductRelationDetail';
// @/api/productcreateProductgetProductupdateProduct
import {createProduct, getProduct, updateProduct} from '@/api/product';
//
const defaultProductParam = {
albumPics: '',
brandId: null,
brandName: '',
deleteStatus: 0,
description: '',
detailDesc: '',
detailHtml: '',
detailMobileHtml: '',
detailTitle: '',
feightTemplateId: 0,
flashPromotionCount: 0,
flashPromotionId: 0,
flashPromotionPrice: 0,
flashPromotionSort: 0,
giftPoint: 0,
giftGrowth: 0,
keywords: '',
lowStock: 0,
name: '',
newStatus: 0,
note: '',
originalPrice: 0,
pic: '',
// ID
memberPriceList: [],
//
productFullReductionList: [{fullPrice: 0, reducePrice: 0}],
//
productLadderList: [{count: 0, discount: 0, price: 0}],
previewStatus: 0,
price: 0,
productAttributeCategoryId: null,
// ID
productAttributeValueList: [],
// SKUSKU
skuStockList: [],
// ID
subjectProductRelationList: [],
// ID
prefrenceAreaProductRelationList: [],
productCategoryId: null,
productCategoryName: '',
productSn: '',
promotionEndTime: '',
promotionPerLimit: 0,
promotionPrice: null,
promotionStartTime: '',
promotionType: 0,
publishStatus: 0,
recommandStatus: 0,
sale: 0,
serviceIds: '',
sort: 0,
stock: 0,
subTitle: '',
unit: '',
usePointLimit: 0,
verifyStatus: 0,
weight: 0
};
export default {
name: 'ProductDetail',
components: {ProductInfoDetail, ProductSaleDetail, ProductAttrDetail, ProductRelationDetail},
props: {
// isEditfalse
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
// 0
active: 0,
// Object.assign
productParam: Object.assign({}, defaultProductParam),
// true
showStatus: [true, false, false, false]
}
},
created() {
// isEdittruegetProductIDproductParam
if (this.isEdit) {
getProduct(this.$route.query.id).then(response => {
this.productParam = response.data;
});
}
},
methods: {
hideAll() {
// showStatusfalse
for (let i = 0; i < this.showStatus.length; i++) {
this.showStatus[i] = false;
}
},
prevStep() {
// 0退1hideAll
if (this.active > 0 && this.active < this.showStatus.length) {
this.active--;
this.hideAll();
this.showStatus[this.active] = true;
}
},
nextStep() {
// 11hideAll
if (this.active < this.showStatus.length - 1) {
this.active++;
this.hideAll();
this.showStatus[this.active] = true;
}
},
finishCommit(isEdit) {
// isEdit
this.$confirm('是否要提交该产品', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (isEdit) {
// isEdittrueupdateProductID
updateProduct(this.$route.query.id, this.productParam).then(response => {
this.$message({
type: 'success',
message: '提交成功',
duration: 1000
});
this.$router.back();
});
} else {
// createProduct
createProduct(this.productParam).then(response => {
this.$message({
type: 'success',
message: '提交成功',
duration: 1000
});
location.reload();
});
}
})
}
}
}
</script>

@ -0,0 +1,384 @@
<template>
<div style="margin-top: 50px">
<el-form :model="value" :rules="rules" ref="productInfoForm" label-width="120px" class="form-inner-container" size="small">
<el-form-item label="商品分类:" prop="productCategoryId">
<el-cascader
v-model="selectProductCateValue"
:options="productCateOptions">
</el-cascader>
</el-form-item>
<el-form-item label="商品名称:" prop="name">
<el-input v-model="value.name"></el-input>
</el-form-item>
<el-form-item label="副标题:" prop="subTitle">
<el-input v-model="value.subTitle"></el-input>
</el-form-item>
<el-form-item label="商品品牌:" prop="brandId">
<el-select
v-model="value.brandId"
@change="handleBrandChange"
placeholder="请选择品牌">
<el-option
v-for="item in brandOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商品介绍:">
<el-input
:autoSize="true"
v-model="value.description"
type="textarea"
placeholder="请输入内容"></el-input>
</el-form-item>
<el-form-item label="商品货号:">
<el-input v-model="value.productSn"></el-input>
</el-form-item>
<el-form-item label="商品售价:">
<el-input v-model="value.price"></el-input>
</el-form-item>
<el-form-item label="市场价:">
<el-input v-model="value.originalPrice"></el-input>
</el-form-item>
<el-form-item label="商品库存:">
<el-input v-model="value.stock"></el-input>
</el-form-item>
<el-form-item label="计量单位:">
<el-input v-model="value.unit"></el-input>
</el-form-item>
<el-form-item label="商品重量:">
<el-input v-model="value.weight" style="width: 300px"></el-input>
<span style="margin-left: 20px"></span>
</el-form-item>
<el-form-item label="排序">
<el-input v-model="value.sort"></el-input>
</el-form-item>
<el-form-item style="text-align: center">
<el-button type="primary" size="medium" @click="handleNext('productInfoForm')"></el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {fetchListWithChildren} from '@/api/productCate'
import {fetchList as fetchBrandList} from '@/api/brand'
import {getProduct} from '@/api/product';
export default {
name: "ProductInfoDetail",
props: {
value: Object,
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
hasEditCreated:false,
//
selectProductCateValue: [],
productCateOptions: [],
brandOptions: [],
rules: {
name: [
{required: true, message: '请输入商品名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
subTitle: [{required: true, message: '请输入商品副标题', trigger: 'blur'}],
productCategoryId: [{required: true, message: '请选择商品分类', trigger: 'blur'}],
brandId: [{required: true, message: '请选择商品品牌', trigger: 'blur'}],
description: [{required: true, message: '请输入商品介绍', trigger: 'blur'}],
requiredProp: [{required: true, message: '该项为必填项', trigger: 'blur'}]
}
};
},
created() {
this.getProductCateList();
this.getBrandList();
},
computed:{
//
productId(){
return this.value.id;
}
},
watch: {
productId:function(newValue){
if(!this.isEdit)return;
if(this.hasEditCreated)return;
if(newValue===undefined||newValue==null||newValue===0)return;
this.handleEditCreated();
},
selectProductCateValue: function (newValue) {
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;
}
}
},
methods: {
//
handleEditCreated(){
if(this.value.productCategoryId!=null){
this.selectProductCateValue.push(this.value.cateParentId);
this.selectProductCateValue.push(this.value.productCategoryId);
}
this.hasEditCreated=true;
},
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});
}
});
},
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});
}
});
},
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;
}
}
}
</script>
<style scoped>
</style>
#abc
<template>
<!-- 外层的 div 元素设置了距离顶部 50px margin用于在页面布局上与其他元素产生间隔 -->
<div style="margin-top: 50px">
<!-- el-form 表单组件用于收集和验证商品相关信息绑定数据模型为 value关联验证规则 rules设置了表单引用名标签宽度类名以及尺寸为小 -->
<el-form :model="value" :rules="rules" ref="productInfoForm" label-width="120px" class="form-inner-container" size="small">
<!-- 表单项标签显示为商品分类通过 prop 属性关联对应的验证规则用于选择商品分类 -->
<el-form-item label="商品分类:" prop="productCategoryId">
<!-- el-cascader 级联选择器组件绑定 v-model selectProductCateValue其选项数据由 productCateOptions 提供用于选择多级的商品分类 -->
<el-cascader
v-model="selectProductCateValue"
:options="productCateOptions">
</el-cascader>
</el-form-item>
<!-- 表单项标签显示为商品名称通过 prop 属性关联对应的验证规则内部使用输入框收集商品名称信息 -->
<el-form-item label="商品名称:" prop="name">
<el-input v-model="value.name"></el-input>
</el-form-item>
<!-- 表单项标签显示为副标题通过 prop 属性关联对应的验证规则内部使用输入框收集商品副标题信息 -->
<el-form-item label="副标题:" prop="subTitle">
<el-input v-model="value.subTitle"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品品牌通过 prop 属性关联对应的验证规则内部使用下拉选择框选择商品品牌并且监听 change 事件 -->
<el-form-item label="商品品牌:" prop="brandId">
<el-select
v-model="value.brandId"
@change="handleBrandChange"
placeholder="请选择品牌">
<!-- 通过循环生成下拉选项每个选项的标签和值根据 brandOptions 数组中的元素来确定 -->
<el-option
v-for="item in brandOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 表单项标签显示为商品介绍内部使用可自动调整大小的文本域输入框收集商品介绍信息 -->
<el-form-item label="商品介绍:">
<el-input
:autoSize="true"
v-model="value.description"
type="textarea"
placeholder="请输入内容"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品货号内部使用输入框收集商品货号信息 -->
<el-form-item label="商品货号:">
<el-input v-model="value.productSn"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品售价内部使用输入框收集商品售价信息 -->
<el-form-item label="商品售价:">
<el-input v-model="value.price"></el-input>
</el-form-item>
<!-- 表单项标签显示为市场价内部使用输入框收集商品市场价信息 -->
<el-form-item label="市场价:">
<el-input v-model="value.originalPrice"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品库存内部使用输入框收集商品库存信息 -->
<el-form-item label="商品库存:">
<el-input v-model="value.stock"></el-input>
</el-form-item>
<!-- 表单项标签显示为计量单位内部使用输入框收集商品计量单位信息 -->
<el-form-item label="计量单位:">
<el-input v-model="value.unit"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品重量内部使用输入框收集商品重量信息并在输入框右侧显示作为单位提示 -->
<el-form-item label="商品重量:">
<el-input v-model="value.weight" style="width: 300px"></el-input>
<span style="margin-left: 20px"></span>
</el-form-item>
<!-- 表单项标签显示为排序内部使用输入框收集商品排序相关信息 -->
<el-form-item label="排序">
<el-input v-model="value.sort"></el-input>
</el-form-item>
<!-- 表单项用于放置操作按钮设置文本居中对齐包含一个下一步按钮点击时触发 handleNext 方法并传入当前表单名称用于进入下一个填写商品促销的步骤 -->
<el-form-item style="text-align: center">
<el-button type="primary" size="medium" @click="handleNext('productInfoForm')"></el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
// @/api/productCate fetchListWithChildren
import {fetchListWithChildren} from '@/api/productCate'
// @/api/brand fetchList fetchBrandList
import {fetchList as fetchBrandList} from '@/api/brand'
// @/api/product getProduct 使
import {getProduct} from '@/api/product';
export default
name: "ProductInfoDetail",
props: {
// value 便
value: Object,
// isEdit false
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
// false
hasEditCreated: false,
//
selectProductCateValue: [],
// ID
productCateOptions: [],
// ID
brandOptions: [],
//
rules: {
name: [
//
{required: true, message: '请输入商品名称', trigger: 'blur'},
// 2 140 2 140
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
subTitle: [
//
{required: true, message: '请输入商品副标题', trigger: 'blur'}
],
productCategoryId: [
//
{required: true, message: '请选择商品分类', trigger: 'blur'}
],
brandId: [
//
{required: true, message: '请选择商品品牌', trigger: 'blur'}
],
description: [
//
{required: true, message: '请输入商品介绍', trigger: 'blur'}
],
requiredProp: [
//
{required: true, message: '该项为必填项', trigger: 'blur'}
]
}
};
},
created() {
// getProductCateList getBrandList
this.getProductCateList();
this.getBrandList();
},
computed: {
// value id undefined
productId() {
return this.value.id;
}
},
watch: {
productId: function (newValue) {
//
if (!this.isEdit) return;
//
if (this.hasEditCreated) return;
// undefinednull 0
if (newValue === undefined || newValue == null || newValue === 0) return;
// handleEditCreated
this.handleEditCreated();
},
selectProductCateValue: function (newValue) {
// null 2
if (newValue!= null && newValue.length === 2) {
// ID ID
this.value.productCategoryId = newValue[1];
// getCateNameById
this.value.productCategoryName = this.getCateNameById(this.value.productCategoryId);
} else {
// ID null
this.value.productCategoryId = null;
this.value.productCategoryName = null;
}
}
},
methods:
//
handleEditCreated()
if (this.value.productCategoryId!= null)
//

@ -0,0 +1,310 @@
<template>
<div style="margin-top: 50px">
<el-form :model="value"
ref="productRelationForm"
label-width="120px"
class="form-inner-container"
size="small">
<el-form-item label="关联专题:">
<el-transfer
style="display: inline-block"
filterable
:filter-method="filterMethod"
filter-placeholder="请输入专题名称"
v-model="selectSubject"
:titles="subjectTitles"
:data="subjectList">
</el-transfer>
</el-form-item>
<el-form-item label="关联优选:">
<el-transfer
style="display: inline-block"
filterable
:filter-method="filterMethod"
filter-placeholder="请输入优选名称"
v-model="selectPrefrenceArea"
:titles="prefrenceAreaTitles"
:data="prefrenceAreaList">
</el-transfer>
</el-form-item>
<el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleFinishCommit"></el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {fetchListAll as fetchSubjectList} from '@/api/subject'
import {fetchList as fetchPrefrenceAreaList} from '@/api/prefrenceArea'
export default {
name: "ProductRelationDetail",
props: {
value: Object,
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
//
subjectList: [],
//
subjectTitles: ['待选择', '已选择'],
//
prefrenceAreaList: [],
//
prefrenceAreaTitles: ['待选择', '已选择']
};
},
created() {
this.getSubjectList();
this.getPrefrenceAreaList();
},
computed:{
//
selectSubject:{
get:function () {
let subjects =[];
if(this.value.subjectProductRelationList==null||this.value.subjectProductRelationList.length<=0){
return subjects;
}
for(let i=0;i<this.value.subjectProductRelationList.length;i++){
subjects.push(this.value.subjectProductRelationList[i].subjectId);
}
return subjects;
},
set:function (newValue) {
this.value.subjectProductRelationList=[];
for(let i=0;i<newValue.length;i++){
this.value.subjectProductRelationList.push({subjectId:newValue[i]});
}
}
},
//
selectPrefrenceArea:{
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]});
}
}
}
},
methods: {
filterMethod(query, item) {
return item.label.indexOf(query) > -1;
},
getSubjectList() {
fetchSubjectList().then(response => {
let list = response.data;
for (let i = 0; i < list.length; i++) {
this.subjectList.push({
label: list[i].title,
key: list[i].id
});
}
});
},
getPrefrenceAreaList() {
fetchPrefrenceAreaList().then(response=>{
let list = response.data;
for (let i = 0; i < list.length; i++) {
this.prefrenceAreaList.push({
label: list[i].name,
key: list[i].id
});
}
});
},
handlePrev(){
this.$emit('prevStep')
},
handleFinishCommit(){
this.$emit('finishCommit',this.isEdit);
}
}
}
</script>
<style scoped>
</style>
#abc
<template>
<!-- 外层的 div 元素设置了距离顶部 50px margin用于在页面布局上与其他元素产生间隔 -->
<div style="margin-top: 50px">
<!-- el-form 表单组件用于收集和展示商品关联相关信息绑定数据模型为 value设置了表单引用名标签宽度类名以及尺寸为小 -->
<el-form :model="value"
ref="productRelationForm"
label-width="120px"
class="form-inner-container"
size="small">
<!-- 表单项标签显示为关联专题用于选择商品关联的专题信息 -->
<el-form-item label="关联专题:">
<!-- el-transfer 穿梭框组件设置为内联块级元素显示可进行过滤操作绑定过滤方法设置过滤占位提示文字双向绑定选中的数据指定左右标题以及数据源 -->
<el-transfer
style="display: inline-block"
filterable
:filter-method="filterMethod"
filter-placeholder="请输入专题名称"
v-model="selectSubject"
:titles="subjectTitles"
:data="subjectList">
</el-transfer>
</el-form-item>
<!-- 表单项标签显示为关联优选用于选择商品关联的优选信息 -->
<el-form-item label="关联优选:">
<!-- el-transfer 穿梭框组件设置为内联块级元素显示可进行过滤操作绑定过滤方法设置过滤占位提示文字双向绑定选中的数据指定左右标题以及数据源 -->
<el-transfer
style="display: inline-block"
filterable
:filter-method="filterMethod"
filter-placeholder="请输入优选名称"
v-model="selectPrefrenceArea"
:titles="prefrenceAreaTitles"
:data="prefrenceAreaList">
</el-transfer>
</el-form-item>
<!-- 表单项用于放置操作按钮设置文本居中对齐包含上一步完成提交商品按钮点击分别触发对应的方法 -->
<el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleFinishCommit"></el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
// @/api/subject fetchListAll fetchSubjectList
import {fetchListAll as fetchSubjectList} from '@/api/subject'
// @/api/prefrenceArea fetchList fetchPrefrenceAreaList
import {fetchList as fetchPrefrenceAreaList} from '@/api/prefrenceArea'
export default {
name: "ProductRelationDetail",
props: {
// value 便
value: Object,
// isEdit false
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
// ID
subjectList: [],
// 穿穿
subjectTitles: ['待选择', '已选择'],
// ID
prefrenceAreaList: [],
// 穿穿
prefrenceAreaTitles: ['待选择', '已选择']
};
},
created() {
// getSubjectList getPrefrenceAreaList
this.getSubjectList();
this.getPrefrenceAreaList();
},
computed: {
// value subjectProductRelationList ID
selectSubject: {
get: function () {
let subjects = [];
if (this.value.subjectProductRelationList == null || this.value.subjectProductRelationList.length <= 0) {
return subjects;
}
for (let i = 0; i < this.value.subjectProductRelationList.length; i++) {
subjects.push(this.value.subjectProductRelationList[i].subjectId);
}
return subjects;
},
set: function (newValue) {
// value subjectProductRelationList ID
this.value.subjectProductRelationList = [];
for (let i = 0; i < newValue.length; i++) {
this.value.subjectProductRelationList.push({subjectId: newValue[i]});
}
}
},
// value prefrenceAreaProductRelationList ID
selectPrefrenceArea: {
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) {
// value prefrenceAreaProductRelationList ID
this.value.prefrenceAreaProductRelationList = [];
for (let i = 0; i < newValue.length; i++) {
this.value.prefrenceAreaProductRelationList.push({prefrenceAreaId: newValue[i]});
}
}
}
},
methods: {
// el-transfer 穿queryitem
filterMethod(query, item) {
return item.label.indexOf(query) > -1;
},
getSubjectList() {
// fetchSubjectList 穿使
fetchSubjectList().then(response => {
let list = response.data;
for (let i = 0; i < list.length; i++) {
this.subjectList.push({
label: list[i].title,
key: list[i].id
});
}
});
},
getPrefrenceAreaList() {
// fetchPrefrenceAreaList 穿使
fetchPrefrenceAreaList().then(response => {
let list = response.data;
for (let i = 0; i < list.length; i++) {
this.prefrenceAreaList.push({
label: list[i].name,
key: list[i].id
});
}
});
},
handlePrev() {
// prevStep 退
this.$emit('prevStep')
},
handleFinishCommit() {
// finishCommit isEdit
this.$emit('finishCommit', this.isEdit);
}
}
}
</script>
<style scoped>
/* 这里的样式部分设置了 scoped 属性,表示样式仅作用于当前组件内部的元素,目前为空,可根据组件的设计需求添加具体的样式规则,比如对表单内各元素、穿梭框等的样式调整,使其在页面中呈现出符合设计预期的外观效果 */
</style>

@ -0,0 +1,601 @@
<template>
<div style="margin-top: 50px">
<el-form :model="value" ref="productSaleForm" label-width="120px" class="form-inner-container" size="small">
<el-form-item label="赠送积分:">
<el-input v-model="value.giftPoint"></el-input>
</el-form-item>
<el-form-item label="赠送成长值:">
<el-input v-model="value.giftGrowth"></el-input>
</el-form-item>
<el-form-item label="积分购买限制:">
<el-input v-model="value.usePointLimit"></el-input>
</el-form-item>
<el-form-item label="预告商品:">
<el-switch
v-model="value.previewStatus"
:active-value="1"
:inactive-value="0">
</el-switch>
</el-form-item>
<el-form-item label="商品上架:">
<el-switch
v-model="value.publishStatus"
:active-value="1"
:inactive-value="0">
</el-switch>
</el-form-item>
<el-form-item label="商品推荐:">
<span style="margin-right: 10px">新品</span>
<el-switch
v-model="value.newStatus"
:active-value="1"
:inactive-value="0">
</el-switch>
<span style="margin-left: 10px;margin-right: 10px">推荐</span>
<el-switch
v-model="value.recommandStatus"
:active-value="1"
:inactive-value="0">
</el-switch>
</el-form-item>
<el-form-item label="服务保证:">
<el-checkbox-group v-model="selectServiceList">
<el-checkbox :label="1">无忧退货</el-checkbox>
<el-checkbox :label="2">快速退款</el-checkbox>
<el-checkbox :label="3">免费包邮</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="详细页标题:">
<el-input v-model="value.detailTitle"></el-input>
</el-form-item>
<el-form-item label="详细页描述:">
<el-input v-model="value.detailDesc"></el-input>
</el-form-item>
<el-form-item label="商品关键字:">
<el-input v-model="value.keywords"></el-input>
</el-form-item>
<el-form-item label="商品备注:">
<el-input v-model="value.note" type="textarea" :autoSize="true"></el-input>
</el-form-item>
<el-form-item label="选择优惠方式:">
<el-radio-group v-model="value.promotionType" size="small">
<el-radio-button :label="0">无优惠</el-radio-button>
<el-radio-button :label="1">特惠促销</el-radio-button>
<el-radio-button :label="2">会员价格</el-radio-button>
<el-radio-button :label="3">阶梯价格</el-radio-button>
<el-radio-button :label="4">满减价格</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-show="value.promotionType===1">
<div>
开始时间
<el-date-picker
v-model="value.promotionStartTime"
type="datetime"
:picker-options="pickerOptions1"
placeholder="选择开始时间">
</el-date-picker>
</div>
<div class="littleMargin">
结束时间
<el-date-picker
v-model="value.promotionEndTime"
type="datetime"
:picker-options="pickerOptions1"
placeholder="选择结束时间">
</el-date-picker>
</div>
<div class="littleMargin">
促销价格
<el-input style="width: 220px" v-model="value.promotionPrice" placeholder="输入促销价格"></el-input>
</div>
</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-input v-model="item.memberPrice" style="width: 200px"></el-input>
</div>
</el-form-item>
<el-form-item v-show="value.promotionType===3">
<el-table :data="value.productLadderList"
style="width: 80%" border>
<el-table-column
label="数量"
align="center"
width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.count"></el-input>
</template>
</el-table-column>
<el-table-column
label="折扣"
align="center"
width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.discount"></el-input>
</template>
</el-table-column>
<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-button type="text" @click="handleRemoveProductLadder(scope.$index, scope.row)">删除</el-button>
<el-button type="text" @click="handleAddProductLadder(scope.$index, scope.row)">添加</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item v-show="value.promotionType===4">
<el-table :data="value.productFullReductionList"
style="width: 80%" border>
<el-table-column
label="满"
align="center"
width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.fullPrice"></el-input>
</template>
</el-table-column>
<el-table-column
label="立减"
align="center"
width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.reducePrice"></el-input>
</template>
</el-table-column>
<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-button type="text" @click="handleRemoveFullReduction(scope.$index, scope.row)">删除</el-button>
<el-button type="text" @click="handleAddFullReduction(scope.$index, scope.row)">添加</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleNext"></el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import {fetchList as fetchMemberLevelList} from '@/api/memberLevel'
export default {
name: "ProductSaleDetail",
props: {
value: Object,
isEdit: {
type: Boolean,
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: {
//
selectServiceList: {
get() {
let list = [];
if (this.value.serviceIds === undefined || this.value.serviceIds == null || this.value.serviceIds === '') return list;
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) {
fullReductionList.push({
fullPrice: 0,
reducePrice: 0
});
} else {
this.$message({
message: '最多只能添加三条',
type: 'warning'
});
}
},
handlePrev() {
this.$emit('prevStep')
},
handleNext() {
this.$emit('nextStep')
}
}
}
</script>
<style scoped>
.littleMargin {
margin-top: 10px;
}
</style>
#abc
<template>
<!-- 外层的 div 元素设置了距离顶部 50px margin用于在页面布局上与其他元素产生间隔 -->
<div style="margin-top: 50px">
<!-- el-form 表单组件用于收集和展示商品促销相关信息绑定数据模型为 value设置了表单引用名标签宽度类名以及尺寸为小 -->
<el-form :model="value" ref="productSaleForm" label-width="120px" class="form-inner-container" size="small">
<!-- 表单项标签显示为赠送积分内部使用输入框收集赠送积分的数量信息 -->
<el-form-item label="赠送积分:">
<el-input v-model="value.giftPoint"></el-input>
</el-form-item>
<!-- 表单项标签显示为赠送成长值内部使用输入框收集赠送成长值的数量信息 -->
<el-form-item label="赠送成长值:">
<el-input v-model="value.giftGrowth"></el-input>
</el-form-item>
<!-- 表单项标签显示为积分购买限制内部使用输入框收集积分购买限制的相关信息 -->
<el-form-item label="积分购买限制:">
<el-input v-model="value.usePointLimit"></el-input>
</el-form-item>
<!-- 表单项标签显示为预告商品内部使用 el-switch 开关组件来控制商品是否为预告状态通过设置不同的激活值和非激活值来表示开关的两种状态 -->
<el-form-item label="预告商品:">
<el-switch
v-model="value.previewStatus"
:active-value="1"
:inactive-value="0">
</el-switch>
</el-form-item>
<!-- 表单项标签显示为商品上架内部使用 el-switch 开关组件来控制商品是否上架通过设置不同的激活值和非激活值来表示开关的两种状态 -->
<el-form-item label="商品上架:">
<el-switch
v-model="value.publishStatus"
:active-value="1"
:inactive-value="0">
</el-switch>
</el-form-item>
<!-- 表单项标签显示为商品推荐包含多个开关组件分别用于控制商品是否为新品以及是否推荐通过设置不同的激活值和非激活值来表示开关的两种状态并在开关之间添加了文字提示间隔 -->
<el-form-item label="商品推荐:">
<span style="margin-right: 10px">新品</span>
<el-switch
v-model="value.newStatus"
:active-value="1"
:inactive-value="0">
</el-switch>
<span style="margin-left: 10px;margin-right: 10px">推荐</span>
<el-switch
v-model="value.recommandStatus"
:active-value="1"
:inactive-value="0">
</el-switch>
</el-form-item>
<!-- 表单项标签显示为服务保证内部使用 el-checkbox-group 复选框组组件提供了几个固定的服务选项供用户选择用于选择商品的服务保证内容 -->
<el-form-item label="服务保证:">
<el-checkbox-group v-model="selectServiceList">
<el-checkbox :label="1">无忧退货</el-checkbox>
<el-checkbox :label="2">快速退款</el-checkbox>
<el-checkbox :label="3">免费包邮</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!-- 表单项标签显示为详细页标题内部使用输入框收集商品详细页标题信息 -->
<el-form-item label="详细页标题:">
<el-input v-model="value.detailTitle"></el-input>
</el-form-item>
<!-- 表单项标签显示为详细页描述内部使用输入框收集商品详细页描述信息 -->
<el-form-item label="详细页描述:">
<el-input v-model="value.detailDesc"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品关键字内部使用输入框收集商品关键字信息 -->
<el-form-item label="商品关键字:">
<el-input v-model="value.keywords"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品备注内部使用可自动调整大小的文本域输入框收集商品备注信息 -->
<el-form-item label="商品备注:">
<el-input v-model="value.note" type="textarea" :autoSize="true"></el-input>
</el-form-item>
<!-- 表单项标签显示为选择优惠方式内部使用 el-radio-group 单选框组组件提供了几种不同的优惠方式选项供用户选择用于确定商品采用的优惠方式 -->
<el-form-item label="选择优惠方式:">
<el-radio-group v-model="value.promotionType" size="small">
<el-radio-button :label="0">无优惠</el-radio-button>
<el-radio-button :label="1">特惠促销</el-radio-button>
<el-radio-button :label="2">会员价格</el-radio-button>
<el-radio-button :label="3">阶梯价格</el-radio-button>
<el-radio-button :label="4">满减价格</el-radio-button>
</el-radio-group>
</el-form-item>
<!-- 根据商品优惠方式promotionType的值判断是否显示当值为 1特惠促销时显示该部分内容用于设置特惠促销的相关时间和价格信息 -->
<el-form-item v-show="value.promotionType===1">
<div>
开始时间
<!-- el-date-picker 日期时间选择器组件绑定 v-model value.promotionStartTime设置选择器类型为 datetime关联日期禁用配置有占位提示文字用于选择特惠促销开始时间 -->
<el-date-picker
v-model="value.promotionStartTime"
type="datetime"
:picker-options="pickerOptions1"
placeholder="选择开始时间">
</el-date-picker>
</div>
<div class="littleMargin">
结束时间
<!-- el-date-picker 日期时间选择器组件绑定 v-model value.promotionEndTime设置选择器类型为 datetime关联日期禁用配置有占位提示文字用于选择特惠促销结束时间 -->
<el-date-picker
v-model="value.promotionEndTime"
type="datetime"
:picker-options="pickerOptions1"
placeholder="选择结束时间">
</el-date-picker>
</div>
<div class="littleMargin">
促销价格
<!-- 输入框组件设置宽度为 220px绑定 v-model value.promotionPrice有占位提示文字用于输入特惠促销价格 -->
<el-input style="width: 220px" v-model="value.promotionPrice" placeholder="输入促销价格"></el-input>
</div>
</el-form-item>
<!-- 根据商品优惠方式promotionType的值判断是否显示当值为 2会员价格时显示该部分内容通过循环展示每个会员等级对应的价格输入框用于设置不同会员等级的商品价格 -->
<el-form-item v-show="value.promotionType===2">
<div v-for="(item, index) in value.memberPriceList" :class="{littleMargin:index!==0}">
{{item.memberLevelName}}
<el-input v-model="item.memberPrice" style="width: 200px"></el-input>
</div>
</el-form-item>
<!-- 根据商品优惠方式promotionType的值判断是否显示当值为 3阶梯价格时显示该部分内容使用 el-table 表格组件展示和操作商品阶梯价格相关信息包含数量折扣等列以及添加删除操作按钮 -->
<el-form-item v-show="value.promotionType===3">
<el-table :data="value.productLadderList"
style="width: 80%" border>
<!-- 表格列标签显示为数量内容居中对齐宽度为 120px内部使用输入框展示和编辑阶梯价格对应的数量信息 -->
<el-table-column
label="数量"
align="center"
width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.count"></el-input>
</template>
</el-table-column>
<!-- 表格列标签显示为折扣内容居中对齐宽度为 120px内部使用输入框展示和编辑阶梯价格对应的折扣信息 -->
<el-table-column
label="折扣"
align="center"
width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.discount"></el-input>
</template>
</el-table-column>
<!-- 表格列标签显示为操作内容居中对齐内部包含删除和添加按钮点击可对当前行的阶梯价格数据进行相应操作 -->
<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-button type="text" @click="handleRemoveProductLadder(scope.$index, scope.row)">删除</el-button>
<el-button type="text" @click="handleAddProductLadder(scope.$index, scope.row)">添加</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 根据商品优惠方式promotionType的值判断是否显示当值为 4满减价格时显示该部分内容使用 el-table 表格组件展示和操作商品满减价格相关信息包含满减条件立减金额等列以及添加删除操作按钮 -->
<el-form-item v-show="value.promotionType===4">
<el-table :data="value.productFullReductionList"
style="width: 80%" border>
<!-- 表格列标签显示为内容居中对齐宽度为 120px内部使用输入框展示和编辑满减价格对应的满金额信息 -->
<el-table-column
label="满"
align="center"
width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.fullPrice"></el-input>
</template>
</el-table-column>
<!-- 表格列标签显示为立减内容居中对齐宽度为 120px内部使用输入框展示和编辑满减价格对应的立减金额信息 -->
<el-table-column
label="立减"
align="center"
width="120">
<template slot-scope="scope">
<el-input v-model="scope.row.reducePrice"></el-input>
</template>
</el-table-column>
<!-- 表格列标签显示为操作内容居中对齐内部包含删除和添加按钮点击可对当前行的满减价格数据进行相应操作 -->
<el-table-column
align="center"
label="操作">
<template slot-scope="scope">
<el-button type="text" @click="handleRemoveFullReduction(scope.$index, scope.row)">删除</el-button>
<el-button type="text" @click="handleAddFullReduction(scope.$index, scope.row)">添加</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 表单项用于放置操作按钮设置文本居中对齐包含上一步下一步按钮点击分别触发对应的方法用于在商品信息填写流程中进行步骤切换 -->
<el-form-item style="text-align: center">
<el-button size="medium" @click="handlePrev"></el-button>
<el-button type="primary" size="medium" @click="handleNext"></el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
// @/api/memberLevel fetchList fetchMemberLevelList
import {fetchList as fetchMemberLevelList} from '@/api/memberLevel'
export default
name: "ProductSaleDetail",
props: {
// value 便
value: Object,
// isEdit false
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
// disabledDate
pickerOptions1: {
disabledDate(time) {
return time.getTime() < Date.now();
}
}
};
},
created() {
if (this.isEdit) {
// handleEditCreated
// this.handleEditCreated();
} else {
// fetchMemberLevelList ID value.memberPriceList
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: {
// value.serviceIds 便
selectServiceList: {
get() {
let list = [];
if (this.value.serviceIds === undefined || this.value.serviceIds == null || this.value.serviceIds === '') return list;
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;
// 0
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;
// 3 0
if (productLadderList.length < 3)
productLadderList.push
count: 0,
discount:

@ -0,0 +1,908 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float: right"
@click="handleSearchList()"
type="primary"
size="small">
查询结果
</el-button>
<el-button
style="float: right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="输入搜索:">
<el-input style="width: 203px" v-model="listQuery.keyword" placeholder="商品名称"></el-input>
</el-form-item>
<el-form-item label="商品货号:">
<el-input style="width: 203px" v-model="listQuery.productSn" placeholder="商品货号"></el-input>
</el-form-item>
<el-form-item label="商品分类:">
<el-cascader
clearable
v-model="selectProductCateValue"
:options="productCateOptions">
</el-cascader>
</el-form-item>
<el-form-item label="商品品牌:">
<el-select v-model="listQuery.brandId" placeholder="请选择品牌" clearable>
<el-option
v-for="item in brandOptions"
: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.publishStatus" placeholder="全部" clearable>
<el-option
v-for="item in publishStatusOptions"
: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.verifyStatus" placeholder="全部" clearable>
<el-option
v-for="item in verifyStatusOptions"
: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-button
class="btn-add"
@click="handleAddProduct()"
size="mini">
添加
</el-button>
</el-card>
<div class="table-container">
<el-table ref="productTable"
: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="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="商品图片" width="120" align="center">
<template slot-scope="scope"><img style="height: 80px" :src="scope.row.pic"></template>
</el-table-column>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">
<p>{{scope.row.name}}</p>
<p>品牌{{scope.row.brandName}}</p>
</template>
</el-table-column>
<el-table-column label="价格/货号" width="120" align="center">
<template slot-scope="scope">
<p>价格{{scope.row.price}}</p>
<p>货号{{scope.row.productSn}}</p>
</template>
</el-table-column>
<el-table-column label="标签" width="140" align="center">
<template slot-scope="scope">
<p>上架
<el-switch
@change="handlePublishStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.publishStatus">
</el-switch>
</p>
<p>新品
<el-switch
@change="handleNewStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.newStatus">
</el-switch>
</p>
<p>推荐
<el-switch
@change="handleRecommendStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.recommandStatus">
</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>
</template>
</el-table-column>
<el-table-column label="操作" width="160" align="center">
<template slot-scope="scope">
<p>
<el-button
size="mini"
@click="handleShowProduct(scope.$index, scope.row)">查看
</el-button>
<el-button
size="mini"
@click="handleUpdateProduct(scope.$index, scope.row)">编辑
</el-button>
</p>
<p>
<el-button
size="mini"
@click="handleShowLog(scope.$index, scope.row)">日志
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</p>
</template>
</el-table-column>
</el-table>
</div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
: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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</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>
</template>
<script>
import {
fetchList,
updateDeleteStatus,
updateNewStatus,
updateRecommendStatus,
updatePublishStatus
} 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 = {
keyword: null,
pageNum: 1,
pageSize: 5,
publishStatus: null,
verifyStatus: null,
productSn: null,
productCategoryId: null,
brandId: null
};
export default {
name: "productList",
data() {
return {
editSkuInfo:{
dialogVisible:false,
productId:null,
productSn:'',
productAttributeCategoryId:null,
stockList:[],
productAttr:[],
keyword:null
},
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() {
this.getList();
this.getBrandList();
this.getProductCateList();
},
watch: {
selectProductCateValue: function (newValue) {
if (newValue != null && newValue.length == 2) {
this.listQuery.productCategoryId = newValue[1];
} else {
this.listQuery.productCategoryId = null;
}
}
},
filters: {
verifyStatusFilter(value) {
if (value === 1) {
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>
<style></style>
#abc
<template>
<!-- 最外层的 div 元素设置了类名为 `app-container`用于包裹整个页面内容作为整体布局的容器 -->
<div class="app-container">
<!-- 使用 Element UI `el-card` 组件创建一个卡片式容器类名为 `filter-container`设置阴影效果为无用于放置筛选搜索相关的内容 -->
<el-card class="filter-container" shadow="never">
<div>
<!-- 使用一个图标元素这里是 Element UI 的搜索图标用于在视觉上提示筛选搜索功能 -->
<i class="el-icon-search"></i>
<!-- 显示筛选搜索文字用于明确功能区域的用途 -->
<span>筛选搜索</span>
<!-- 使用 Element UI `el-button` 按钮组件设置样式为右浮动点击时触发 `handleSearchList` 方法按钮类型为主要按钮通常有突出显示效果尺寸为小用于执行查询结果的操作 -->
<el-button
style="float: right"
@click="handleSearchList()"
type="primary"
size="small">
查询结果
</el-button>
<!-- 使用 Element UI `el-button` 按钮组件设置样式为右浮动且与右侧有 15px 的间隔点击时触发 `handleResetSearch` 方法尺寸为小用于执行重置筛选条件的操作 -->
<el-button
style="float: right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<!-- 使用 Element UI `el-form` 表单组件设置表单为内联模式表单元素在一行显示绑定数据模型为 `listQuery`尺寸为小标签宽度为 140px用于放置各种筛选条件输入框 -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单项标签显示为输入搜索内部使用 `el-input` 输入框组件设置宽度为 203px双向绑定 `listQuery.keyword` 数据有占位提示文字商品名称用于输入搜索关键词按商品名称进行筛选 -->
<el-form-item label="输入搜索:">
<el-input style="width: 203px" v-model="listQuery.keyword" placeholder="商品名称"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品货号内部使用 `el-input` 输入框组件设置宽度为 203px双向绑定 `listQuery.productSn` 数据有占位提示文字商品货号用于输入商品货号进行筛选 -->
<el-form-item label="商品货号:">
<el-input style="width: 203px" v-model="listQuery.productSn" placeholder="商品货号"></el-input>
</el-form-item>
<!-- 表单项标签显示为商品分类内部使用 `el-cascader` 级联选择器组件设置可清除选择内容双向绑定 `selectProductCateValue` 数据其选项数据由 `productCateOptions` 提供用于选择商品分类进行筛选 -->
<el-form-item label="商品分类:">
<el-cascader
clearable
v-model="selectProductCateValue"
:options="productCateOptions">
</el-cascader>
</el-form-item>
<!-- 表单项标签显示为商品品牌内部使用 `el-select` 下拉选择框组件双向绑定 `listQuery.brandId` 数据有占位提示文字请选择品牌设置可清除选择内容其下拉选项通过循环 `brandOptions` 数组生成用于选择商品品牌进行筛选 -->
<el-form-item label="商品品牌:">
<el-select v-model="listQuery.brandId" placeholder="请选择品牌" clearable>
<el-option
v-for="item in brandOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 表单项标签显示为上架状态内部使用 `el-select` 下拉选择框组件双向绑定 `listQuery.publishStatus` 数据有占位提示文字全部设置可清除选择内容其下拉选项由 `publishStatusOptions` 数组提供用于选择商品上架状态进行筛选 -->
<el-form-item label="上架状态:">
<el-select v-model="listQuery.publishStatus" placeholder="全部" clearable>
<el-option
v-for="item in publishStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 表单项标签显示为审核状态内部使用 `el-select` 下拉选择框组件双向绑定 `listQuery.verifyStatus` 数据有占位提示文字全部设置可清除选择内容其下拉选项由 `verifyStatusOptions` 数组提供用于选择商品审核状态进行筛选 -->
<el-form-item label="审核状态:">
<el-select v-model="listQuery.verifyStatus" placeholder="全部" clearable>
<el-option
v-for="item in verifyStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-form>
</div>
</el-card>
<!-- 使用 Element UI `el-card` 组件创建一个卡片式容器类名为 `operate-container`设置阴影效果为无用于放置数据列表操作相关的内容 -->
<el-card class="operate-container" shadow="never">
<!-- 使用一个图标元素这里是 Element UI 的票务图标可能用于视觉上提示与数据操作相关的功能 -->
<i class="el-icon-tickets"></i>
<!-- 显示数据列表文字用于明确功能区域的用途 -->
<span>数据列表</span>
<!-- 使用 Element UI `el-button` 按钮组件设置类名为 `btn-add`点击时触发 `handleAddProduct` 方法尺寸为迷你型用于执行添加商品的操作 -->
<el-button
class="btn-add"
@click="handleAddProduct()"
size="mini">
添加
</el-button>
</el-card>
<!-- 一个 div 元素设置类名为 `table-container`用于包裹数据列表表格作为表格展示的容器 -->
<div class="table-container">
<!-- 使用 Element UI `el-table` 表格组件设置了表格引用名为 `productTable`绑定数据为 `list`宽度为 100%监听行选择变化事件 `selection-change` 并触发 `handleSelectionChange` 方法根据 `listLoading` 变量控制加载状态显示设置表格边框用于展示商品数据列表 -->
<el-table ref="productTable"
:data="list"
style="width: 100%"
@selection-change="handleSelectionChange"
v-loading="listLoading"
border>
<!-- 表格列类型为选择列可勾选行宽度为 60px内容居中对齐用于选择表格中的行数据 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 表格列标签显示为编号宽度为 100px内容居中对齐通过插槽作用域获取当前行数据展示商品编号信息 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 表格列标签显示为商品图片宽度为 120px内容居中对齐通过插槽作用域获取当前行数据展示商品图片信息图片设置了高度为 80px -->
<el-table-column label="商品图片" width="120" align="center">
<template slot-scope="scope"><img style="height: 80px" :src="scope.row.pic"></template>
</el-table-column>
<!-- 表格列标签显示为商品名称内容居中对齐通过插槽作用域获取当前行数据展示商品名称以及品牌信息 -->
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">
<p>{{scope.row.name}}</p>
<p>品牌{{scope.row.brandName}}</p>
</template>
</el-table-column>
<!-- 表格列标签显示为价格/货号宽度为 120px内容居中对齐通过插槽作用域获取当前行数据展示商品价格和货号信息 -->
<el-table-column label="价格/货号" width="120" align="center">
<template slot-scope="scope">
<p>价格{{scope.row.price}}</p>
<p>货号{{scope.row.productSn}}</p>
</template>
</el-table-column>
<!-- 表格列标签显示为标签宽度为 140px内容居中对齐通过插槽作用域获取当前行数据包含多个开关组件用于展示和操作商品的上架新品推荐等状态信息 -->
<el-table-column label="标签" width="140" align="center">
<template slot-scope="scope">
<p>上架
<!-- 使用 `el-switch` 开关组件监听开关状态变化事件 `change` 并触发对应的方法设置激活值和非激活值双向绑定当前行的 `publishStatus` 数据用于控制商品上架状态 -->
<el-switch
@change="handlePublishStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.publishStatus">
</el-switch>
</p>
<p>新品
<!-- 使用 `el-switch` 开关组件监听开关状态变化事件 `change` 并触发对应的方法设置激活值和非激活值双向绑定当前行的 `newStatus` 数据用于控制商品是否为新品状态 -->
<el-switch
@change="handleNewStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.newStatus">
</el-switch>
</p>
<p>推荐
<!-- 使用 `el-switch` 开关组件监听开关状态变化事件 `change` 并触发对应的方法设置激活值和非激活值双向绑定当前行的 `recommandStatus` 数据用于控制商品是否为推荐状态 -->
<el-switch
@change="handleRecommendStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.recommandStatus">
</el-switch>
</p>
</template>
</el-table-column>
<!-- 表格列标签显示为排序宽度为 100px内容居中对齐通过插槽作用域获取当前行数据展示商品排序信息 -->
<el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<!-- 表格列标签显示为SKU 库存宽度为 100px内容居中对齐通过插槽作用域获取当前行数据包含一个编辑按钮点击触发 `handleShowSkuEditDialog` 方法用于查看和编辑商品 SKU 库存信息 -->
<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>
<!-- 表格列标签显示为销量宽度为 100px内容居中对齐通过插槽作用域获取当前行数据展示商品销量信息 -->
<el-table-column label="销量" width="100" align="center">
<template slot-scope="scope">{{scope.row.sale}}</template>
</el-table-column>
<!-- 表格列标签显示为审核状态宽度为 100px内容居中对齐通过插槽作用域获取当前行数据展示商品审核状态信息同时包含一个查看审核详情的按钮点击触发 `handleShowVerifyDetail` 方法 -->
<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>
</template>
</el-table-column>
<!-- 表格列标签显示为操作宽度为 160px内容居中对齐通过插槽作用域获取当前行数据包含多个操作按钮如查看编辑查看日志删除等分别触发对应的方法用于对商品进行各种操作 -->
<el-table-column label="操作" width="160" align="center">
<template slot-scope="scope">
<p>
<el-button
size="mini"
@click="handleShowProduct(scope.$index, scope.row)">查看
</el-button>
<el-button
size="mini"
@click="handleUpdateProduct(scope.$index, scope.row)">编辑
</el-button>
</p>
<p>
<el-button
size="mini"
@click="handleShowLog(scope.$index, scope.row)">日志
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</p>
</template>
</el-table-column>
</el-table>
</div>
<!-- 一个 div 元素设置类名为 `batch-operate-container`用于放置批量操作相关的内容如选择批量操作类型和执行操作的按钮 -->
<div class="batch-operate-container">
<!-- 使用 Element UI `el-select` 下拉选择框组件尺寸为小双向绑定 `operateType` 数据有占位提示文字批量操作其下拉选项通过循环 `operates` 数组生成用于选择批量操作的类型 -->
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<!-- 使用 Element UI `el-button` 按钮组件设置样式为左距 20px类名为 `search-button`点击时触发 `handleBatchOperate` 方法按钮类型为主要按钮通常有突出显示效果尺寸为小用于执行确定批量操作的动作 -->
<el-button
style="margin-left: 20px"
class="search-button"
@click="handleBatchOperate()"
type="primary"
size="small">
确定
</el-button>
</div>
<!-- 一个 div 元素设置类名为 `pagination-container`用于包裹分页组件作为分页功能的容器 -->
<div class="pagination-container">
<!-- 使用 Element UI `el-pagination` 分页组件设置背景色样式监听页面尺寸变化和当前页变化事件并分别触发对应的方法设置分页布局样式绑定页面尺寸可选页面尺寸数组当前页码以及总数据量等数据用于实现数据列表的分页功能 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<!-- 使用 Element UI `el-dialog` 对话框组件设置标题为编辑货品信息通过双向绑定控制对话框的显示隐藏状态宽度设置为 40%用于展示和编辑商品 SKU 库存相关的详细信息 -->
<el-dialog
title="编辑货品信息"
:visible.sync="editSkuInfo.dialogVisible"
width="40%">
<span>商品货号

@ -0,0 +1,30 @@
<template> 
<product-detail :is-edit='true'></product-detail>
</template>
<script>
import ProductDetail from './components/ProductDetail'
export default {
name: 'updateProduct',
components: { ProductDetail }
}
</script>
<style>
</style>
#abc
<template>
<!-- 此处使用了名为 `product-detail` 的自定义组件通过 `:is-edit='true'` 属性绑定 `product-detail` 组件传递了一个名为 `is-edit` 的属性值为 `true`意味着当前是编辑商品的操作模式与新增商品模式相区别`product-detail` 组件应该会根据这个属性来展示相应的编辑界面以及执行对应的逻辑 -->
<product-detail :is-edit='true'></product-detail>
</template>
<script>
// `./components/` `ProductDetail`
import ProductDetail from './components/ProductDetail';
export default {
name: 'updateProduct',
// `components` `ProductDetail` 使`updateProduct`使 `<product-detail>` Vue.js 便使
components: { ProductDetail }
}
</script>
<style>
/* 这里的样式部分目前为空,可根据 `updateProduct` 组件整体的视觉呈现需求,添加相应的 CSS 样式规则,例如对整个组件布局进行调整,设置组件内各元素的字体、颜色、间距等样式属性,使其在页面中展现出符合预期的外观效果 */
</style>

@ -0,0 +1,32 @@
<template>
<product-attr-detail :is-edit='false'></product-attr-detail>
</template>
<script>
import ProductAttrDetail from './components/ProductAttrDetail'
export default {
name: 'addProductAttr',
components: { ProductAttrDetail }
}
</script>
<style scoped>
</style>
#abc
<template>
<!-- 使用名为 `product-attr-detail` 的自定义组件通过 `:is-edit='false'` 属性绑定向该组件传递了名为 `is-edit` 的属性值设为 `false`表示当前处于添加商品属性的操作模式区别于编辑商品属性模式`product-attr-detail` 组件会依据这个属性值来展示对应的界面以及执行相应的添加相关逻辑 -->
<product-attr-detail :is-edit='false'></product-attr-detail>
</template>
<script>
// `./components/` `ProductAttrDetail`
import ProductAttrDetail from './components/ProductAttrDetail';
export default {
name: 'addProductAttr',
// `components` `ProductAttrDetail` 使 `addProductAttr` `<product-attr-detail>` Vue.js 便
components: { ProductAttrDetail }
}
</script>
<style scoped>
/* 这里样式部分设置了 `scoped` 属性,意味着样式只会应用到当前组件内部的元素上。目前该部分样式为空,后续可以根据 `addProductAttr` 组件的视觉设计要求,添加对应的 CSS 样式规则,像设置组件内文本的字体、颜色,表单元素的布局、边框样式等,让组件在页面中呈现出期望的外观效果 */
</style>

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

@ -0,0 +1,463 @@
<template> 
<div class="app-container">
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets" style="margin-top: 5px"></i>
<span style="margin-top: 5px">数据列表</span>
<el-button
class="btn-add"
@click="addProductAttrCate()"
size="mini">
添加
</el-button>
</el-card>
<div class="table-container">
<el-table ref="productAttrCateTable"
style="width: 100%"
:data="list"
v-loading="listLoading"
border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="类型名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="属性数量" width="200" align="center">
<template slot-scope="scope">{{scope.row.attributeCount==null?0:scope.row.attributeCount}}</template>
</el-table-column>
<el-table-column label="参数数量" width="200" align="center">
<template slot-scope="scope">{{scope.row.paramCount==null?0:scope.row.paramCount}}</template>
</el-table-column>
<el-table-column label="设置" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="getAttrList(scope.$index, scope.row)">属性列表
</el-button>
<el-button
size="mini"
@click="getParamList(scope.$index, scope.row)">参数列表
</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-container">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<el-dialog
:title="dialogTitle"
:visible.sync="dialogVisible"
:before-close="handleClose()"
width="30%">
<el-form ref="productAttrCatForm":model="productAttrCate" :rules="rules" label-width="120px">
<el-form-item label="类型名称" prop="name">
<el-input v-model="productAttrCate.name" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleConfirm('productAttrCatForm')"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,createProductAttrCate,deleteProductAttrCate,updateProductAttrCate} from '@/api/productAttrCate'
export default {
name: 'productAttrCateList',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
pageNum: 1,
pageSize: 5
},
dialogVisible: false,
dialogTitle:'',
productAttrCate:{
name:'',
id:null
},
rules: {
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) {
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(response=>{
this.$message({
message: '删除成功',
type: 'success',
duration:1000
});
this.getList();
});
});
},
handleUpdate(index, row) {
this.dialogVisible = true;
this.dialogTitle = "编辑类型";
this.productAttrCate.name = row.name;
this.productAttrCate.id = row.id;
},
getAttrList(index, row) {
this.$router.push({path: '/pms/productAttrList',query:{cid:row.id,cname:row.name,type:0}})
},
getParamList(index, row) {
this.$router.push({path: '/pms/productAttrList',query:{cid:row.id,cname:row.name,type:1}})
},
handleConfirm(formName){
this.$refs[formName].validate((valid) => {
if (valid) {
let data = new URLSearchParams();
data.append("name",this.productAttrCate.name);
if(this.dialogTitle==="添加类型"){
createProductAttrCate(data).then(response=>{
this.$message({
message: '添加成功',
type: 'success',
duration:1000
});
this.dialogVisible = false;
this.getList();
});
}else{
updateProductAttrCate(this.productAttrCate.id,data).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration:1000
});
this.dialogVisible = false;
this.getList();
});
}
} else {
console.log('error submit!!');
return false;
}
});
},
handleClose(){
if (!this.dialogVisible && this.$refs.productAttrCatForm) {
this.$refs.productAttrCatForm.clearValidate()
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
</style>
#abc
<template>
<!-- 最外层的 div 元素设置了类名为 `app-container`作为整个页面内容的容器用于整体布局和包裹内部的各个功能模块 -->
<div class="app-container">
<!-- 使用 Element UI `el-card` 组件创建一个卡片式容器类名为 `operate-container`设置阴影效果为无用于放置与数据列表操作相关的内容比如添加按钮等 -->
<el-card class="operate-container" shadow="never">
<!-- 使用 Element UI 的图标组件展示票务图标并设置了距顶部 5px margin用于在视觉上提示与数据操作相关的功能起到一定的表意作用 -->
<i class="el-icon-tickets" style="margin-top: 5px"></i>
<!-- 显示数据列表文字同样设置了距顶部 5px margin用于明确此卡片容器内操作主要针对的数据列表功能区域 -->
<span style="margin-top: 5px">数据列表</span>
<!-- 使用 Element UI `el-button` 按钮组件设置类名为 `btn-add`点击时触发 `addProductAttrCate` 方法尺寸为迷你型用于执行添加商品属性分类的操作 -->
<el-button
class="btn-add"
@click="addProductAttrCate()"
size="mini">
添加
</el-button>
</el-card>
<!-- 一个 div 元素设置类名为 `table-container`作为表格展示的容器用于包裹下面的 `el-table` 组件呈现数据列表内容 -->
<div class="table-container">
<!-- 使用 Element UI `el-table` 表格组件设置了表格引用名为 `productAttrCateTable`宽度为 100%绑定数据为 `list`根据 `listLoading` 变量控制加载状态显示设置表格边框用于展示商品属性分类相关的数据列表 -->
<el-table ref="productAttrCateTable"
style="width: 100%"
:data="list"
v-loading="listLoading"
border>
<!-- 表格列标签显示为编号宽度为 100px内容居中对齐通过插槽作用域获取当前行数据展示商品属性分类的编号信息 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 表格列标签显示为类型名称内容居中对齐通过插槽作用域获取当前行数据展示商品属性分类的类型名称信息 -->
<el-table-column label="类型名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 表格列标签显示为属性数量宽度为 200px内容居中对齐通过插槽作用域获取当前行数据展示商品属性分类对应的属性数量信息如果数量为 `null`则显示为 0 -->
<el-table-column label="属性数量" width="200" align="center">
<template slot-scope="scope">{{scope.row.attributeCount==null?0:scope.row.attributeCount}}</template>
</el-table-column>
<!-- 表格列标签显示为参数数量宽度为 200px内容居中对齐通过插槽作用域获取当前行数据展示商品属性分类对应的参数数量信息如果数量为 `null`则显示为 0 -->
<el-table-column label="参数数量" width="200" align="center">
<template slot-scope="scope">{{scope.row.paramCount==null?0:scope.row.paramCount}}</template>
</el-table-column>
<!-- 表格列标签显示为设置宽度为 200px内容居中对齐通过插槽作用域获取当前行数据包含两个按钮分别点击可跳转到对应的属性列表和参数列表页面用于查看相应详细信息 -->
<el-table-column label="设置" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="getAttrList(scope.$index, scope.row)">属性列表
</el-button>
<el-button
size="mini"
@click="getParamList(scope.$index, scope.row)">参数列表
</el-button>
</template>
</el-table-column>
<!-- 表格列标签显示为操作宽度为 200px内容居中对齐通过插槽作用域获取当前行数据包含编辑和删除两个按钮分别点击可触发对应的编辑和删除操作方法 -->
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 一个 div 元素设置类名为 `pagination-container`用于包裹分页组件作为分页功能的容器 -->
<div class="pagination-container">
<!-- 使用 Element UI `el-pagination` 分页组件设置背景色样式监听页面尺寸变化和当前页变化事件并分别触发对应的方法设置分页布局样式绑定页面尺寸可选页面尺寸数组当前页码以及总数据量等数据用于实现数据列表的分页功能 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<!-- 使用 Element UI `el-dialog` 对话框组件通过双向绑定控制对话框的标题显示隐藏状态以及关闭前的操作宽度设置为 30%用于弹出添加或编辑商品属性分类的表单界面 -->
<el-dialog
:title="dialogTitle"
:visible.sync="dialogVisible"
:before-close="handleClose()"
width="30%">
<!-- 使用 Element UI `el-form` 表单组件设置了表单引用名绑定数据模型为 `productAttrCate`关联表单验证规则 `rules`标签宽度为 120px用于放置添加或编辑商品属性分类的输入框等表单元素 -->
<el-form ref="productAttrCatForm":model="productAttrCate" :rules="rules" label-width="120px">
<!-- 表单项标签显示为类型名称指定了验证属性 `prop` `name`内部使用 `el-input` 输入框组件双向绑定 `productAttrCate.name` 数据设置自动补全功能为关闭用于输入商品属性分类的名称 -->
<el-form-item label="类型名称" prop="name">
<el-input v-model="productAttrCate.name" auto-complete="off"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<!-- 使用 Element UI `el-button` 按钮组件点击时隐藏对话框用于取消添加或编辑操作 -->
<el-button @click="dialogVisible = false"> </el-button>
<!-- 使用 Element UI `el-button` 按钮组件点击时触发 `handleConfirm` 方法并传入表单引用名按钮类型为主要按钮通常有突出显示效果用于确认添加或编辑操作 -->
<el-button type="primary" @click="handleConfirm('productAttrCatForm')"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
// `@/api/productAttrCate` API
import {fetchList,createProductAttrCate,deleteProductAttrCate,updateProductAttrCate} from '@/api/productAttrCate';
export default {
name: 'productAttrCateList',
data() {
return {
// `null`
list: null,
// `null`
total: null,
// `true` `false`
listLoading: true,
//
listQuery: {
pageNum: 1,
pageSize: 5
},
// `false`
dialogVisible: false,
//
dialogTitle: '',
// IDID `null`
productAttrCate: {
name: '',
id: null
},
// `name` `blur`
rules: {
name: [
{ required: true, message: '请输入类型名称', trigger: 'blur' }
]
}
};
},
created() {
// `getList`
this.getList();
},
methods: {
getList() {
// `listLoading` `true`
this.listLoading = true;
// `fetchList` `listQuery` `listLoading` `false` `list` `total`
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
addProductAttrCate() {
// `dialogVisible` `true`使 `dialogTitle`
this.dialogVisible = true;
this.dialogTitle = "添加类型";
},
handleSizeChange(val) {
// `listQuery` `pageNum` 1 `pageSize` `val` `getList`
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
// `listQuery` `pageNum` `val` `getList`
this.listQuery.pageNum = val;
this.getList();
},
handleDelete(index, row) {
// `deleteProductAttrCate` `id` `getList`
this.$confirm('是否要删除该品牌', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteProductAttrCate(row.id).then(response => {
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
this.getList();
});
});
},
handleUpdate(index, row) {
// `dialogVisible` `true`使 `dialogTitle` ID `productAttrCate`
this.dialogVisible = true;
this.dialogTitle = "编辑类型";
this.productAttrCate.name = row.name;
this.productAttrCate.id = row.id;
},
getAttrList(index, row) {
// `$router.push` `/pms/productAttrList` `cid` ID`cname` `type` 0
this.$router.push({path: '/pms/productAttrList',query:{cid:row.id,cname:row.name,type:0}})
},
getParamList(index, row) {
// `$router.push` `/pms/productAttrList` `cid` ID`cname` `type` 1
this.$router.push({path: '/pms/productAttrList',query:{cid:row.id,cname:row.name,type:1}})
},
handleConfirm(formName) {
// `$refs[formName].validate` `valid` `true``dialogTitle` `createProductAttrCate` `updateProductAttrCate` ID `valid` `false` `false`
this.$refs[formName].validate((valid) => {
if (valid) {
let data = new URLSearchParams();
data.append("name",this.productAttrCate.name);
if(this.dialogTitle==="添加类型"){
createProductAttrCate(data).then(response => {
this.$message({
message: '添加成功',
type: 'success',
duration: 1000
});
this.dialogVisible = false;
this.getList();
});
}else{
updateProductAttrCate(this.productAttrCate.id,data).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
this.dialogVisible = false;
this.getList();
});
}
} else {
console.log('error submit!!');
return false;
}
});
},
handleClose() {
// `$refs.productAttrCatForm` `clearValidate`
if (!this.dialogVisible && this.$refs.productAttrCatForm) {
this.$refs.productAttrCatForm.clearValidate()
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>

@ -0,0 +1,455 @@
<template> 
<div class="app-container">
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets" style="margin-top: 5px"></i>
<span style="margin-top: 5px">数据列表</span>
<el-button
class="btn-add"
@click="addProductAttr()"
size="mini">
添加
</el-button>
</el-card>
<div class="table-container">
<el-table ref="productAttrTable"
: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="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="属性名称" width="140" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="商品类型" width="140" align="center">
<template slot-scope="scope">{{$route.query.cname}}</template>
</el-table-column>
<el-table-column label="属性是否可选" width="120" align="center">
<template slot-scope="scope">{{scope.row.selectType|selectTypeFilter}}</template>
</el-table-column>
<el-table-column label="属性值的录入方式" width="150" align="center">
<template slot-scope="scope">{{scope.row.inputType|inputTypeFilter}}</template>
</el-table-column>
<el-table-column label="可选值列表" align="center">
<template slot-scope="scope">{{scope.row.inputList}}</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="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
: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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import {fetchList, deleteProductAttr} from '@/api/productAttr'
export default {
name: 'productAttrList',
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
pageNum: 1,
pageSize: 5,
type: this.$route.query.type
},
operateType: null,
multipleSelection: [],
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 < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
if (this.operateType !== 'deleteProductAttr') {
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);
}
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();
});
});
},
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>
</style>
#abc
<template>
<!-- 最外层的 div 元素类名为 `app-container`作为整个页面内容的容器用于包裹内部各个功能模块实现整体页面布局 -->
<div class="app-container">
<!-- 使用 Element UI `el-card` 组件创建一个卡片式容器类名为 `operate-container`设置阴影效果为 `never`无阴影用于放置与数据列表操作相关的元素比如操作按钮等 -->
<el-card class="operate-container" shadow="never">
<!-- 使用 Element UI 的图标组件展示票务图标并设置 `margin-top: 5px`使其在垂直方向上距顶部有一定间距在视觉上辅助提示该区域与数据操作相关 -->
<i class="el-icon-tickets" style="margin-top: 5px"></i>
<!-- 显示数据列表文字同样设置 `margin-top: 5px`明确该卡片容器内的操作是针对下方展示的数据列表起到功能说明作用 -->
<span style="margin-top: 5px">数据列表</span>
<!-- 使用 Element UI `el-button` 按钮组件设置类名为 `btn-add`点击时触发 `addProductAttr` 方法尺寸为 `mini`迷你型用于执行添加商品属性的操作 -->
<el-button
class="btn-add"
@click="addProductAttr()"
size="mini">
添加
</el-button>
</el-card>
<!-- div 元素类名为 `table-container`作为表格展示的容器用于包裹 `el-table` 组件使其在页面中以特定样式展示数据列表 -->
<div class="table-container">
<!-- 使用 Element UI `el-table` 表格组件设置引用名为 `productAttrTable`绑定数据为 `list`宽度为 `100%`监听 `selection-change` 事件当表格行选择状态改变时并触发 `handleSelectionChange` 方法根据 `listLoading` 变量控制加载状态显示如显示加载动画等设置表格边框样式用于展示商品属性相关的数据列表 -->
<el-table ref="productAttrTable"
:data="list"
style="width: 100%"
@selection-change="handleSelectionChange"
v-loading="listLoading"
border>
<!-- 表格列类型为 `selection`选择列宽度为 `60px`内容在单元格中居中对齐用于在表格中提供勾选行的功能方便进行批量操作等选择行的场景 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 表格列标签显示为编号宽度为 `100px`内容在单元格中居中对齐通过插槽作用域`slot-scope`获取当前行数据展示商品属性的编号信息 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 表格列标签显示为属性名称宽度为 `140px`内容在单元格中居中对齐通过插槽作用域获取当前行数据展示商品属性的名称信息 -->
<el-table-column label="属性名称" width="140" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 表格列标签显示为商品类型宽度为 `140px`内容在单元格中居中对齐通过路由对象`$route`的查询参数`query`获取 `cname` 的值来展示商品类型信息表明该属性所属的商品类型 -->
<el-table-column label="商品类型" width="140" align="center">
<template slot-scope="scope">{{$route.query.cname}}</template>
</el-table-column>
<!-- 表格列标签显示为属性是否可选宽度为 `120px`内容在单元格中居中对齐通过管道符`|`使用 `selectTypeFilter` 过滤器对 `scope.row.selectType` 的值进行处理后展示用于以更友好的文字形式展示属性可选类型如单选多选等 -->
<el-table-column label="属性是否可选" width="120" align="center">
<template slot-scope="scope">{{scope.row.selectType|selectTypeFilter}}</template>
</el-table-column>
<!-- 表格列标签显示为属性值的录入方式宽度为 `150px`内容在单元格中居中对齐通过管道符使用 `inputTypeFilter` 过滤器对 `scope.row.inputType` 的值进行处理后展示用于以更友好的文字形式展示属性值录入方式如从列表中选取手工录入等 -->
<el-table-column label="属性值的录入方式" width="150" align="center">
<template slot-scope="scope">{{scope.row.inputType|inputTypeFilter}}</template>
</el-table-column>
<!-- 表格列标签显示为可选值列表内容在单元格中居中对齐通过插槽作用域获取当前行数据直接展示商品属性的可选值列表信息 -->
<el-table-column label="可选值列表" align="center">
<template slot-scope="scope">{{scope.row.inputList}}</template>
</el-table-column>
<!-- 表格列标签显示为排序宽度为 `100px`内容在单元格中居中对齐通过插槽作用域获取当前行数据展示商品属性在排序方面的相关信息比如用于确定属性展示顺序等 -->
<el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<!-- 表格列标签显示为操作宽度为 `200px`内容在单元格中居中对齐通过插槽作用域获取当前行数据包含编辑和删除两个按钮分别点击可触发对应的 `handleUpdate` `handleDelete` 方法用于对单个商品属性进行编辑或删除操作 -->
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- div 元素类名为 `batch-operate-container`用于放置批量操作相关的元素如选择批量操作类型的下拉框和执行操作的按钮 -->
<div class="batch-operate-container">
<!-- 使用 Element UI `el-select` 下拉选择框组件尺寸为 `small`小型双向绑定 `operateType` 数据设置占位提示文字为批量操作其下拉选项通过循环 `operates` 数组生成用于选择批量操作的具体类型 -->
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<!-- 使用 Element UI `el-button` 按钮组件设置样式 `margin-left: 20px`距左侧 20px 间距类名为 `search-button`点击时触发 `handleBatchOperate` 方法按钮类型为 `primary`主要按钮通常有突出显示效果尺寸为 `small`用于执行确定批量操作的动作 -->
<el-button
style="margin-left: 20px"
class="search-button"
@click="handleBatchOperate()"
type="primary"
size="small">
确定
</el-button>
</div>
<!-- div 元素类名为 `pagination-container`作为分页功能的容器用于包裹 `el-pagination` 分页组件实现数据列表的分页展示 -->
<div class="pagination-container">
<!-- 使用 Element UI `el-pagination` 分页组件设置 `background`背景色样式监听 `size-change`页面尺寸变化 `current-change`当前页码变化事件并分别触发对应的 `handleSizeChange` `handleCurrentChange` 方法设置分页布局样式绑定页面尺寸可选页面尺寸数组当前页码以及总数据量等数据实现数据列表的分页功能 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
// `@/api/productAttr` `fetchList` `deleteProductAttr` API
import {fetchList, deleteProductAttr} from '@/api/productAttr';
export default {
name: 'productAttrList',
data() {
return {
// `null`
list: null,
// `null`
total: null,
// `true` `false`
listLoading: true,
// `type`
listQuery: {
pageNum: 1,
pageSize: 5,
type: this.$route.query.type
},
// `null`
operateType: null,
// `selection-change` 便
multipleSelection: [],
// `label` `value`
operates: [
{
label: "删除",
value: "deleteProductAttr"
}
]
};
},
created() {
// `getList`
this.getList();
},
methods: {
getList() {
// `listLoading` `true`
this.listLoading = true;
// `fetchList` `cid` ID `listQuery` `listLoading` `false` `list` `total`
fetchList(this.$route.query.cid, this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
addProductAttr() {
// `$router.push` `/pms/addProductAttr` `cid` `type`
this.$router.push({path:'/pms/addProductAttr',query:{cid:this.$route.query.cid,type:this.$route.query.type}});
},
handleSelectionChange(val) {
// `multipleSelection` 便
this.multipleSelection = val;
},
handleBatchOperate() {
// `multipleSelection` 1 `deleteProductAttr` `id` `handleDeleteProductAttr`
if (this.multipleSelection < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
if (this.operateType!== 'deleteProductAttr') {
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);
}
this.handleDeleteProductAttr(ids);
},
handleSizeChange(val) {
// `listQuery` `pageNum` 1 `pageSize` `val` `getList`
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
// `listQuery` `pageNum` `val` `getList`
this.listQuery.pageNum = val;
this.getList();
},
handleUpdate(index, row) {
// `$router.push` `/pms/updateProductAttr` `id` ID
this.$router.push({path:'/pms/updateProductAttr',query:{id:row.id}});
},
handleDeleteProductAttr(ids) {
// `URLSearchParams` `ids` `deleteProductAttr` `getList`
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();
});
});
},
handleDelete(index, row) {
// `ids` `id` `handleDeleteProductAttr`
let ids = [];
ids.push(row.id);
this.handleDeleteProductAttr(ids);
}
},
filters:
// `inputType`

@ -0,0 +1,32 @@
<template>
<product-attr-detail :is-edit='true'></product-attr-detail>
</template>
<script>
import ProductAttrDetail from './components/ProductAttrDetail'
export default {
name: 'updateProductAttr',
components: { ProductAttrDetail }
}
</script>
<style scoped>
</style>
#abc
<template>
<!-- 使用名为 `product-attr-detail` 的自定义组件通过 `:is-edit='true'` 属性绑定向该组件传递了名为 `is-edit` 的属性值设为 `true`表示当前处于编辑商品属性的操作模式区别于新增商品属性模式`product-attr-detail` 组件会依据这个属性值来展示对应的编辑界面以及执行相应的编辑相关逻辑 -->
<product-attr-detail :is-edit='true'></product-attr-detail>
</template>
<script>
// `./components/` `ProductAttrDetail`
import ProductAttrDetail from './components/ProductAttrDetail';
export default {
name: 'updateProductAttr',
// `components` `ProductAttrDetail` 使 `updateProductAttr` `<product-attr-detail>` Vue.js 便
components: { ProductAttrDetail }
}
</script>
<style scoped>
/* 这里样式部分设置了 `scoped` 属性,意味着样式只会应用到当前组件内部的元素上。目前该部分样式为空,后续可以根据 `updateProductAttr` 组件的视觉设计要求,添加对应的 CSS 样式规则,像设置组件内文本的字体、颜色,表单元素的布局、边框样式等,让组件在页面中呈现出期望的外观效果 */
</style>

@ -0,0 +1,31 @@
<template> 
<product-cate-detail :is-edit='false'></product-cate-detail>
</template>
<script>
import ProductCateDetail from './components/ProductCateDetail'
export default {
name: 'addProductCate',
components: { ProductCateDetail }
}
</script>
<style>
</style>
#abc
<template>
<!-- 使用名为 `product-cate-detail` 的自定义组件通过 `:is-edit='false'` 属性绑定 `product-cate-detail` 组件传递了 `is-edit` 属性其值为 `false`这表明当前处于添加商品分类的操作模式与编辑商品分类模式相区分`product-cate-detail` 组件会基于这个属性值展示出对应的添加商品分类界面并执行相应的添加相关逻辑 -->
<product-cate-detail :is-edit='false'></product-cate-detail>
</template>
<script>
// `./components/` `ProductCateDetail` `./components/`
import ProductCateDetail from './components/ProductCateDetail';
export default {
name: 'addProductCate',
// `components` `ProductCateDetail` `addProductCate` `<product-cate-detail>` Vue.js 便便
components: { ProductCateDetail }
}
</script>
<style>
/* 这里的样式部分目前为空,后续可以依据 `addProductCate` 组件整体的视觉呈现要求,添加相应的 CSS 样式规则,例如设置组件在页面中的整体布局样式(如宽度、高度、外边距、内边距等),对组件内文本的字体、字号、颜色等样式属性进行定义,还可以针对各种表单元素(如果有的话)设置其边框、背景色、圆角等外观样式,从而让组件在页面中展现出符合预期的视觉效果 */
</style>

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

@ -0,0 +1,456 @@
<template>
<div class="app-container">
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets" style="margin-top: 5px"></i>
<span style="margin-top: 5px">数据列表</span>
<el-button
class="btn-add"
@click="handleAddProductCate()"
size="mini">
添加
</el-button>
</el-card>
<div class="table-container">
<el-table ref="productCateTable"
style="width: 100%"
:data="list"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="分类名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="级别" width="100" align="center">
<template slot-scope="scope">{{scope.row.level | levelFilter}}</template>
</el-table-column>
<el-table-column label="商品数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.productCount }}</template>
</el-table-column>
<el-table-column label="数量单位" width="100" align="center">
<template slot-scope="scope">{{scope.row.productUnit }}</template>
</el-table-column>
<el-table-column label="导航栏" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleNavStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.navStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleShowStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.showStatus">
</el-switch>
</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="设置" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
:disabled="scope.row.level | disableNextLevel"
@click="handleShowNextLevel(scope.$index, scope.row)">查看下级
</el-button>
<el-button
size="mini"
@click="handleTransferProduct(scope.$index, scope.row)">转移商品
</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-container">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import {fetchList,deleteProductCate,updateShowStatus,updateNavStatus} from '@/api/productCate'
export default {
name: "productCateList",
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
pageNum: 1,
pageSize: 5
},
parentId: 0
}
},
created() {
this.resetParentId();
this.getList();
},
watch: {
$route(route) {
this.resetParentId();
this.getList();
}
},
methods: {
resetParentId(){
this.listQuery.pageNum = 1;
if (this.$route.query.parentId != null) {
this.parentId = this.$route.query.parentId;
} else {
this.parentId = 0;
}
},
handleAddProductCate() {
this.$router.push('/pms/addProductCate');
},
getList() {
this.listLoading = true;
fetchList(this.parentId, this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleNavStatusChange(index, row) {
let data = new URLSearchParams();
let ids=[];
ids.push(row.id)
data.append('ids',ids);
data.append('navStatus',row.navStatus);
updateNavStatus(data).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
handleShowStatusChange(index, row) {
let data = new URLSearchParams();
let ids=[];
ids.push(row.id)
data.append('ids',ids);
data.append('showStatus',row.showStatus);
updateShowStatus(data).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
handleShowNextLevel(index, row) {
this.$router.push({path: '/pms/productCate', query: {parentId: row.id}})
},
handleTransferProduct(index, row) {
console.log('handleAddProductCate');
},
handleUpdate(index, row) {
this.$router.push({path:'/pms/updateProductCate',query:{id:row.id}});
},
handleDelete(index, row) {
this.$confirm('是否要删除该品牌', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteProductCate(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>
<style scoped>
</style>
#abc
<template>
<!-- 最外层的 `div` 元素设置了类名为 `app-container`作为整个页面内容的容器用于包裹内部各个功能模块实现整体页面布局 -->
<div class="app-container">
<!-- 使用 Element UI `el-card` 组件创建一个卡片式容器类名为 `operate-container`设置阴影效果为 `never`无阴影用于放置与数据列表操作相关的元素比如操作按钮等 -->
<el-card class="operate-container" shadow="never">
<!-- 使用 Element UI 的图标组件展示票务图标并设置 `margin-top: 5px`使其在垂直方向上距顶部有一定间距在视觉上辅助提示该区域与数据操作相关 -->
<i class="el-icon-tickets" style="margin-top: 5px"></i>
<!-- 显示数据列表文字同样设置 `margin-top: 5px`明确该卡片容器内的操作是针对下方展示的数据列表起到功能说明作用 -->
<span style="margin-top: 5px">数据列表</span>
<!-- 使用 Element UI `el-button` 按钮组件设置类名为 `btn-add`点击时触发 `handleAddProductCate` 方法尺寸为 `mini`迷你型用于执行添加商品分类的操作 -->
<el-button
class="btn-add"
@click="handleAddProductCate()"
size="mini">
添加
</el-button>
</el-card>
<!-- `div` 元素类名为 `table-container`作为表格展示的容器用于包裹 `el-table` 组件使其在页面中以特定样式展示数据列表 -->
<div class="table-container">
<!-- 使用 Element UI `el-table` 表格组件设置引用名为 `productCateTable`宽度为 `100%`绑定数据为 `list`根据 `v-loading` 指令结合 `listLoading` 变量控制加载状态显示如显示加载动画等设置表格边框样式用于展示商品分类相关的数据列表 -->
<el-table ref="productCateTable"
style="width: 100%"
:data="list"
v-loading="listLoading" border>
<!-- 表格列标签显示为编号宽度为 `100px`内容在单元格中居中对齐通过插槽作用域`slot-scope`获取当前行数据展示商品分类的编号信息 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 表格列标签显示为分类名称内容在单元格中居中对齐通过插槽作用域获取当前行数据展示商品分类的名称信息 -->
<el-table-column label="分类名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 表格列标签显示为级别宽度为 `100px`内容在单元格中居中对齐通过管道符`|`使用 `levelFilter` 过滤器对 `scope.row.level` 的值进行处理后展示用于以更友好的文字形式一级二级展示商品分类的级别信息 -->
<el-table-column label="级别" width="100" align="center">
<template slot-scope="scope">{{scope.row.level | levelFilter}}</template>
</el-table-column>
<!-- 表格列标签显示为商品数量宽度为 `100px`内容在单元格中居中对齐通过插槽作用域获取当前行数据展示该商品分类下包含的商品数量信息 -->
<el-table-column label="商品数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.productCount }}</template>
</el-table-column>
<!-- 表格列标签显示为数量单位宽度为 `100px`内容在单元格中居中对齐通过插槽作用域获取当前行数据展示商品数量对应的单位信息 -->
<el-table-column label="数量单位" width="100" align="center">
<template slot-scope="scope">{{scope.row.productUnit }}</template>
</el-table-column>
<!-- 表格列标签显示为导航栏宽度为 `100px`内容在单元格中居中对齐通过插槽作用域获取当前行数据包含一个 `el-switch` 开关组件监听开关状态变化事件 `change` 并触发 `handleNavStatusChange` 方法双向绑定 `scope.row.navStatus` 数据用于控制商品分类在导航栏的显示状态如是否展示在导航栏等 -->
<el-table-column label="导航栏" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleNavStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.navStatus">
</el-switch>
</template>
</el-table-column>
<!-- 表格列标签显示为是否显示宽度为 `100px`内容在单元格中居中对齐通过插槽作用域获取当前行数据包含一个 `el-switch` 开关组件监听开关状态变化事件 `change` 并触发 `handleShowStatusChange` 方法双向绑定 `scope.row.showStatus` 数据用于控制商品分类是否显示可能在页面其他位置的显示与否等情况 -->
<el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleShowStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.showStatus">
</el-switch>
</template>
</el-table-column>
<!-- 表格列标签显示为排序宽度为 `100px`内容在单元格中居中对齐通过插槽作用域获取当前行数据展示商品分类在排序方面的相关信息比如用于确定分类展示顺序等 -->
<el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort }}</template>
</el-table-column>
<!-- 表格列标签显示为设置宽度为 `200px`内容在单元格中居中对齐通过插槽作用域获取当前行数据包含两个按钮查看下级按钮根据 `scope.row.level` 通过 `disableNextLevel` 过滤器判断是否禁用如果是一级分类可能允许查看下级等逻辑点击触发 `handleShowNextLevel` 方法转移商品按钮点击触发 `handleTransferProduct` 方法用于相关的商品转移操作目前方法内仅打印日志可能后续完善具体逻辑 -->
<el-table-column label="设置" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
:disabled="scope.row.level | disableNextLevel"
@click="handleShowNextLevel(scope.$index, scope.row)">查看下级</ </el-button>
<el-button
size="mini"
@click="handleTransferProduct(scope.$index, scope.row)">转移商品</ </el-button>
</template>
</el-table-column>
<!-- 表格列标签显示为操作宽度为 `200px`内容在单元格中居中对齐通过插槽作用域获取当前行数据包含编辑和删除两个按钮分别点击可触发对应的 `handleUpdate` `handleDelete` 方法用于对单个商品分类进行编辑或删除操作 -->
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
@click="handleUpdate(scope.$index, scope.row)">编辑</ </el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)">删除</ </el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- `div` 元素类名为 `pagination-container`作为分页功能的容器用于包裹 `el-pagination` 分页组件实现数据列表的分页展示 -->
<div class="pagination-container">
<!-- 使用 Element UI `el-pagination` 分页组件设置 `background`背景色样式监听 `size-change`页面尺寸变化 `current-change`当前页码变化事件并分别触发对应的 `handleSizeChange` `handleCurrentChange` 方法设置分页布局样式绑定页面尺寸可选页面尺寸数组当前页码以及总数据量等数据实现数据列表的分页功能 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
// `@/api/productCate` `fetchList``deleteProductCate``updateShowStatus``updateNavStatus` API
import {fetchList,deleteProductCate,updateShowStatus,updateNavStatus} from '@/api/productCate';
export default {
name: "productCateList",
data() {
return {
// `null`
list: null,
// `null`
total: null,
// `true` `false`
listLoading: true,
//
listQuery: {
pageNum: 1,
pageSize: 5
},
// ID `0`
parentId: 0
};
},
created() {
// `resetParentId` ID `getList`
this.resetParentId();
this.getList();
},
watch: {
// `$route` `resetParentId` ID `getList`
$route(route) {
this.resetParentId();
this.getList();
}
},
methods: {
resetParentId() {
// `listQuery` `pageNum` `1` `parentId` `null` `parentId` `parentId` `null` `parentId` `0` ID
this.listQuery.pageNum = 1;
if (this.$route.query.parentId!= null) {
this.parentId = this.$route.query.parentId;
} else {
this.parentId = 0;
}
},
handleAddProductCate() {
// `$router.push` `/pms/addProductCate`
this.$router.push('/pms/addProductCate');
},
getList() {
// `listLoading` `true`
this.listLoading = true;
// `fetchList` `parentId` ID `listQuery` `listLoading` `false` `list` `total`
fetchList(this.parentId, this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
handleSizeChange(val) {
// `listQuery` `pageNum` `1` `pageSize` `val` `getList`
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
// `listQuery` `pageNum` `val` `getList`
this.listQuery.pageNum = val;
this.getList();
},
handleNavStatusChange(index, row) {
// `URLSearchParams` `id` `ids` `navStatus` `updateNavStatus`
let data = new URLSearchParams();
let ids=[];
ids.push(row.id)
data.append('ids',ids);
data.append('navStatus',row.navStatus);
updateNavStatus(data).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
handleShowStatusChange(index, row) {
// `URLSearchParams` `id` `ids` `showStatus` `updateShowStatus`
let data = new URLSearchParams();
let ids=[];
ids.push(row.id)
data.append('ids',ids);
data.append('showStatus',row.showStatus);
updateShowStatus(data).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
handleShowNextLevel(index, row) {
// `$router.push` `/pms/productCate` `parentId` `id`
this.$router.push({path: '/pms/productCate', query: {parentId: row.id}})
},
handleTransferProduct(index, row) {
// `handleAddProductCate`
console.log('handleAddProductCate');
},
handleUpdate(index, row)
// `$router.push

@ -0,0 +1,31 @@
<template> 
<product-cate-detail :is-edit='true'></product-cate-detail>
</template>
<script>
import ProductCateDetail from './components/ProductCateDetail'
export default {
name: 'updateProductCate',
components: { ProductCateDetail }
}
</script>
<style>
</style>
#abc
<template>
<!-- 使用名为 `product-cate-detail` 的自定义组件并通过 `:is-edit='true'` 进行属性绑定传递了 `is-edit` 属性且其值设为 `true`这意味着当前处于编辑商品分类的操作模式与新增商品分类的模式相区别`product-cate-detail` 组件会依据这个属性值来展示对应的编辑界面以及执行相应的编辑相关逻辑例如展示已有的商品分类信息以供修改并处理保存修改后的数据等操作 -->
<product-cate-detail :is-edit='true'></product-cate-detail>
</template>
<script>
// `./components/` `ProductCateDetail` `./components/`
import ProductCateDetail from './components/ProductCateDetail';
export default {
name: 'updateProductCate',
// `components` `ProductCateDetail` 使 `updateProductCate` `<product-cate-detail>` Vue.js 便
components: { ProductCateDetail }
}
</script>
<style>
/* 这里的样式部分目前为空,后续可以根据 `updateProductCate` 组件整体的视觉呈现要求,添加相应的 CSS 样式规则。例如,可以设置组件在页面中的整体布局样式(如宽度、高度、外边距、内边距等),对组件内文本的字体、字号、颜色等样式属性进行定义,还能针对各种表单元素(如果有的话)设置其边框、背景色、圆角等外观样式,从而让组件在页面中展现出符合预期的视觉效果 */
</style>

@ -0,0 +1,27 @@
<template>
<home-advertise-detail :isEdit="false"></home-advertise-detail>
</template>
<script>
import HomeAdvertiseDetail from './components/HomeAdvertiseDetail'
export default {
name: 'addHomeAdvertise',
components: { HomeAdvertiseDetail }
}
</script>
<style></style>
#abc
<template>
<!-- 使用HomeAdvertiseDetail组件并传递isEdit属性为false -->
<home-advertise-detail :isEdit="false"></home-advertise-detail>
</template>
import HomeAdvertiseDetail from './components/HomeAdvertiseDetail' // HomeAdvertiseDetail
export default {
name: 'addHomeAdvertise', // addHomeAdvertise
components: { HomeAdvertiseDetail } // HomeAdvertiseDetail
}
<style></style> <!-- 空的样式标签 -->

@ -0,0 +1,421 @@
<template>
<el-card class="form-container" shadow="never">
<el-form :model="homeAdvertise"
:rules="rules"
ref="homeAdvertiseFrom"
label-width="150px"
size="small">
<el-form-item label="广告名称:" prop="name">
<el-input v-model="homeAdvertise.name" class="input-width"></el-input>
</el-form-item>
<el-form-item label="广告位置:">
<el-select v-model="homeAdvertise.type">
<el-option
v-for="type in typeOptions"
:key="type.value"
:label="type.label"
:value="type.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="开始时间:" prop="startTime">
<el-date-picker
type="datetime"
placeholder="选择日期"
v-model="homeAdvertise.startTime"></el-date-picker>
</el-form-item>
<el-form-item label="到期时间:" prop="endTime">
<el-date-picker
type="datetime"
placeholder="选择日期"
v-model="homeAdvertise.endTime"></el-date-picker>
</el-form-item>
<el-form-item label="上线/下线:">
<el-radio-group v-model="homeAdvertise.status">
<el-radio :label="0">下线</el-radio>
<el-radio :label="1">上线</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="广告图片:">
<single-upload v-model="homeAdvertise.pic"></single-upload>
</el-form-item>
<el-form-item label="排序:">
<el-input v-model="homeAdvertise.sort" class="input-width"></el-input>
</el-form-item>
<el-form-item label="广告链接:" prop="url">
<el-input v-model="homeAdvertise.url" class="input-width"></el-input>
</el-form-item>
<el-form-item label="广告备注:">
<el-input
class="input-width"
type="textarea"
:rows="5"
placeholder="请输入内容"
v-model="homeAdvertise.note">
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('homeAdvertiseFrom')"></el-button>
<el-button v-if="!isEdit" @click="resetForm('homeAdvertiseFrom')"></el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
import SingleUpload from '@/components/Upload/singleUpload'
import {createHomeAdvertise, getHomeAdvertise, updateHomeAdvertise} from '@/api/homeAdvertise'
const defaultTypeOptions = [
{
label: 'PC首页轮播',
value: 0
},
{
label: 'APP首页轮播',
value: 1
}
];
const defaultHomeAdvertise = {
name: null,
type: 1,
pic: null,
startTime: null,
endTime: null,
status: 0,
url: null,
note: null,
sort: 0
};
export default {
name: 'HomeAdvertiseDetail',
components:{SingleUpload},
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
homeAdvertise: null,
rules: {
name: [
{required: true, message: '请输入广告名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
url: [
{required: true, message: '请输入广告链接', trigger: 'blur'}
],
startTime: [
{required: true, message: '请选择开始时间', trigger: 'blur'}
],
endTime: [
{required: true, message: '请选择到期时间', trigger: 'blur'}
],
pic: [
{required: true, message: '请选择广告图片', trigger: 'blur'}
]
},
typeOptions: Object.assign({}, defaultTypeOptions)
}
},
created(){
if (this.isEdit) {
getHomeAdvertise(this.$route.query.id).then(response => {
this.homeAdvertise = response.data;
});
}else{
this.homeAdvertise = Object.assign({},defaultHomeAdvertise);
}
},
methods: {
onSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$confirm('是否提交数据', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateHomeAdvertise(this.$route.query.id, this.homeAdvertise).then(response => {
this.$refs[formName].resetFields();
this.$message({
message: '修改成功',
type: 'success',
duration:1000
});
this.$router.back();
});
} else {
createHomeAdvertise(this.homeAdvertise).then(response => {
this.$refs[formName].resetFields();
this.homeAdvertise = Object.assign({},defaultHomeAdvertise);
this.$message({
message: '提交成功',
type: 'success',
duration:1000
});
});
}
});
} else {
this.$message({
message: '验证失败',
type: 'error',
duration:1000
});
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.homeAdvertise = Object.assign({},defaultHomeAdvertise);
}
}
}
</script>
<style scoped>
.input-width {
width: 70%;
}
</style>
#abc
<template>
<!-- 使用el-card组件创建一个卡片式的容器设置其类名为form-container并且阴影效果为never即无阴影 -->
<el-card class="form-container" shadow="never">
<!-- 使用el-form组件创建表单绑定数据模型为homeAdvertise设置表单验证规则为rules
给表单设置ref属性用于后续在JavaScript中获取表单实例标签宽度为150px表单尺寸为small -->
<el-form :model="homeAdvertise"
:rules="rules"
ref="homeAdvertiseFrom"
label-width="150px"
size="small">
<!-- el-form-item用于定义表单中的每一项这里的标签显示为广告名称并通过prop属性关联对应的验证规则 -->
<el-form-item label="广告名称:" prop="name">
<!-- el-input组件用于输入文本通过v-model双向绑定数据到homeAdvertise对象的name属性设置类名为input-width -->
<el-input v-model="homeAdvertise.name" class="input-width"></el-input>
</el-form-item>
<!-- 另一个el-form-item标签显示为广告位置 -->
<el-form-item label="广告位置:">
<!-- el-select组件用于创建下拉选择框通过v-model双向绑定数据到homeAdvertise对象的type属性 -->
<el-select v-model="homeAdvertise.type">
<!-- 通过v-for循环遍历typeOptions数组来生成el-option选项每个选项的keylabelvalue分别对应相应的属性值 -->
<el-option
v-for="type in typeOptions"
:key="type.value"
:label="type.label"
:value="type.value">
</el-option>
</el-select>
</el-form-item>
<!-- el-form-item用于开始时间这一项标签显示为开始时间并通过prop关联对应的验证规则 -->
<el-form-item label="开始时间:" prop="startTime">
<!-- el-date-picker组件用于选择日期和时间类型设置为datetime即日期时间选择器
占位文本为选择日期通过v-model双向绑定数据到homeAdvertise对象的startTime属性 -->
<el-date-picker
type="datetime"
placeholder="选择日期"
v-model="homeAdvertise.startTime"></el-date-picker>
</el-form-item>
<!-- 类似的这是到期时间对应的el-form-item -->
<el-form-item label="到期时间:" prop="endTime">
<!-- el-date-picker用于选择到期时间同样是日期时间选择器绑定到homeAdvertise对象的endTime属性 -->
<el-date-picker
type="datetime"
placeholder="选择日期"
v-model="homeAdvertise.endTime"></el-date-picker>
</el-form-item>
<!-- el-form-item用于上线/下线这一项 -->
<el-form-item label="上线/下线:">
<!-- el-radio-group组件用于创建单选按钮组通过v-model双向绑定数据到homeAdvertise对象的status属性 -->
<el-radio-group v-model="homeAdvertise.status">
<!-- 定义一个单选按钮标签为下线值为0 -->
<el-radio :label="0">下线</el-radio>
<!-- 定义另一个单选按钮标签为上线值为1 -->
<el-radio :label="1">上线</el-radio>
</el-radio-group>
</el-form-item>
<!-- 广告图片对应的el-form-item -->
<el-form-item label="广告图片:">
<!-- 引入了一个名为single-upload的自定义组件通过v-model双向绑定数据到homeAdvertise对象的pic属性 -->
<single-upload v-model="homeAdvertise.pic"></single-upload>
</el-form-item>
<!-- 排序对应的el-form-item -->
<el-form-item label="排序:">
<!-- el-input组件用于输入排序相关的数值绑定到homeAdvertise对象的sort属性设置类名为input-width -->
<el-input v-model="homeAdvertise.sort" class="input-width"></el-input>
</el-form-item>
<!-- 广告链接对应的el-form-item通过prop关联对应的验证规则 -->
<el-form-item label="广告链接:" prop="url">
<!-- el-input组件用于输入广告链接文本绑定到homeAdvertise对象的url属性设置类名为input-width -->
<el-input v-model="homeAdvertise.url" class="input-width"></el-input>
</el-form-item>
<!-- 广告备注对应的el-form-item -->
<el-form-item label="广告备注:">
<!-- el-input组件用于输入多行文本设置为textarea类型设置行数为5占位文本为请输入内容
绑定到homeAdvertise对象的note属性 -->
<el-input
class="input-width"
type="textarea"
:rows="5"
placeholder="请输入内容"
v-model="homeAdvertise.note">
</el-input>
</el-form-item>
<!-- 一个普通的el-form-item用于放置按钮等操作元素 -->
<el-form-item>
<!-- el-button组件按钮类型为primary主要按钮样式点击时调用onSubmit方法并传入'homeAdvertiseFrom'参数 -->
<el-button type="primary" @click="onSubmit('homeAdvertiseFrom')"></el-button>
<!-- 条件渲染的el-button!isEdit为真即处于非编辑状态时显示点击时调用resetForm方法并传入'homeAdvertiseFrom'参数 -->
<el-button v-if="!isEdit" @click="resetForm('homeAdvertiseFrom')"></el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
// SingleUpload'@/components/Upload/singleUpload'
import SingleUpload from '@/components/Upload/singleUpload'
// '@/api/homeAdvertise'createHomeAdvertisegetHomeAdvertiseupdateHomeAdvertise
import {createHomeAdvertise, getHomeAdvertise, updateHomeAdvertise} from '@/api/homeAdvertise'
// 广广
const defaultTypeOptions = [
{
label: 'PC首页轮播',
value: 0
},
{
label: 'APP首页轮播',
value: 1
}
];
// homeAdvertise
const defaultHomeAdvertise = {
name: null,
type: 1,
pic: null,
startTime: null,
endTime: null,
status: 0,
url: null,
note: null,
sort: 0
};
export default {
name: 'HomeAdvertiseDetail',
components:{SingleUpload},
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
// 广
homeAdvertise: null,
rules: {
name: [
// 广广
{required: true, message: '请输入广告名称', trigger: 'blur'},
// 广2140
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
url: [
// 广广
{required: true, message: '请输入广告链接', trigger: 'blur'}
],
startTime: [
//
{required: true, message: '请选择开始时间', trigger: 'blur'}
],
endTime: [
//
{required: true, message: '请选择到期时间', trigger: 'blur'}
],
pic: [
// 广广
{required: true, message: '请选择广告图片', trigger: 'blur'}
]
},
typeOptions: Object.assign({}, defaultTypeOptions)
}
},
created(){
//
if (this.isEdit) {
// getHomeAdvertise广id
// homeAdvertise
getHomeAdvertise(this.$route.query.id).then(response => {
this.homeAdvertise = response.data;
});
}else{
// homeAdvertisehomeAdvertise
this.homeAdvertise = Object.assign({},defaultHomeAdvertise);
}
},
methods: {
onSubmit(formName) {
// validatevalid
this.$refs[formName].validate((valid) => {
if (valid) {
//
this.$confirm('是否提交数据', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
// updateHomeAdvertise广idhomeAdvertise
//
updateHomeAdvertise(this.$route.query.id, this.homeAdvertise).then(response => {
this.$refs[formName].resetFields();
this.$message({
message: '修改成功',
type: 'success',
duration:1000
});
this.$router.back();
});
} else {
// createHomeAdvertise广homeAdvertise
// homeAdvertise
createHomeAdvertise(this.homeAdvertise).then(response => {
this.$refs[formName].resetFields();
this.homeAdvertise = Object.assign({},defaultHomeAdvertise);
this.$message({
message: '提交成功',
type: 'success',
duration:1000
});
});
}
});
} else {
//
this.$message({
message: '验证失败',
type: 'error',
duration:1000
});
return false;
}
});
},
resetForm(formName) {
// resetFieldshomeAdvertise
this.$refs[formName].resetFields();
this.homeAdvertise = Object.assign({},defaultHomeAdvertise);
}
}
}
</script>
<style scoped>
/* 定义类名为input-width的样式设置宽度为70% */
.input-width {
width: 70%;
}
</style>

@ -0,0 +1,637 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="广告名称:">
<el-input v-model="listQuery.name" class="input-width" placeholder="广告名称"></el-input>
</el-form-item>
<el-form-item label="广告位置:">
<el-select v-model="listQuery.type" placeholder="全部" clearable class="input-width">
<el-option v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="到期时间:">
<el-date-picker
class="input-width"
v-model="listQuery.endTime"
value-format="yyyy-MM-dd"
type="date"
placeholder="请选择时间">
</el-date-picker>
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button size="mini" class="btn-add" @click="handleAdd()">广</el-button>
</el-card>
<div class="table-container">
<el-table ref="homeAdvertiseTable"
: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="120" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="广告名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="广告位置" width="120" align="center">
<template slot-scope="scope">{{scope.row.type | formatType}}</template>
</el-table-column>
<el-table-column label="广告图片" width="120" align="center">
<template slot-scope="scope"><img style="height: 80px" :src="scope.row.pic"></template>
</el-table-column>
<el-table-column label="时间" width="220" align="center">
<template slot-scope="scope">
<p>开始时间{{scope.row.startTime | formatTime}}</p>
<p>到期时间{{scope.row.endTime | formatTime}}</p>
</template>
</el-table-column>
<el-table-column label="上线/下线" width="120" align="center">
<template slot-scope="scope">
<el-switch
@change="handleUpdateStatus(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.status">
</el-switch>
</template>
</el-table-column>
<el-table-column label="点击次数" width="120" align="center">
<template slot-scope="scope">{{scope.row.clickCount}}</template>
</el-table-column>
<el-table-column label="生成订单" width="120" align="center">
<template slot-scope="scope">{{scope.row.orderCount}}</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
: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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import {fetchList,updateStatus,deleteHomeAdvertise} from '@/api/homeAdvertise';
import {formatDate} from '@/utils/date';
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
name: null,
type: null,
endTime:null
};
const defaultTypeOptions = [
{
label: 'PC首页轮播',
value: 0
},
{
label: 'APP首页轮播',
value: 1
}
];
export default {
name: 'homeAdvertiseList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
typeOptions: Object.assign({}, defaultTypeOptions),
list: null,
total: null,
listLoading: false,
multipleSelection: [],
operates: [
{
label: "删除",
value: 0
}
],
operateType: null
}
},
created() {
this.getList();
},
filters:{
formatType(type){
if(type===1){
return 'APP首页轮播';
}else{
return 'PC首页轮播';
}
},
formatTime(time){
if(time==null||time===''){
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSelectionChange(val){
this.multipleSelection = val;
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleUpdateStatus(index,row){
this.$confirm('是否要修改上线/下线状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id,{status:row.status}).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'success',
message: '已取消操作!'
});
this.getList();
});
},
handleDelete(index,row){
this.deleteHomeAdvertise(row.id);
},
handleBatchOperate(){
if (this.multipleSelection < 1) {
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);
}
if(this.operateType===0){
//
this.deleteHomeAdvertise(ids);
}else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
}
},
handleAdd(){
this.$router.push({path: '/sms/addAdvertise'})
},
handleUpdate(index,row){
this.$router.push({path: '/sms/updateAdvertise', query: {id: row.id}})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
})
},
deleteHomeAdvertise(ids){
this.$confirm('是否要删除该广告?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
deleteHomeAdvertise(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '删除成功!'
});
});
})
}
}
}
</script>
<style scoped>
.input-width {
width: 203px;
}
</style>
#abc
<template>
<!-- 定义一个Vue组件的模板部分根元素是一个class为app-container的div -->
<div class="app-container">
<!-- 使用Element UI的el-card组件创建一个筛选搜索的容器设置阴影效果为never -->
<el-card class="filter-container" shadow="never">
<div>
<!-- 使用Element UI的图标组件展示搜索图标 -->
<i class="el-icon-search"></i>
<!-- 显示筛选搜索的文字说明 -->
<span>筛选搜索</span>
<!-- 使用Element UI的按钮组件设置样式为右浮动类型为主要按钮绑定点击事件handleSearchList按钮大小为small用于查询搜索操作 -->
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<!-- 使用Element UI的按钮组件设置样式为右浮动且距右侧15px绑定点击事件handleResetSearch按钮大小为small用于重置操作 -->
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<!-- 使用Element UI的表单组件设置为行内表单绑定数据模型为listQuery表单尺寸为small标签宽度为140px -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单中的一个表单项用于输入广告名称绑定v-model到listQuery.name设置输入框样式类为input-width并设置占位文本 -->
<el-form-item label="广告名称:">
<el-input v-model="listQuery.name" class="input-width" placeholder="广告名称"></el-input>
</el-form-item>
<!-- 表单中的一个表单项用于选择广告位置绑定v-model到listQuery.type设置占位文本为全部可清空样式类为input-width -->
<el-form-item label="广告位置:">
<el-select v-model="listQuery.type" placeholder="全部" clearable class="input-width">
<!-- 循环遍历typeOptions数组生成下拉选项每个选项根据对应的item设置keylabel和value属性 -->
<el-option v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 表单中的一个表单项用于选择到期时间绑定v-model到listQuery.endTime设置日期格式为yyyy-MM-dd类型为日期选择器设置占位文本 -->
<el-form-item label="到期时间:">
<el-date-picker
class="input-width"
v-model="listQuery.endTime"
value-format="yyyy-MM-dd"
type="date"
placeholder="请选择时间">
</el-date-picker>
</el-form-item>
</el-form>
</div>
</el-card>
<!-- 使用Element UI的el-card组件创建一个操作相关的容器设置阴影效果为never -->
<el-card class="operate-container" shadow="never">
<!-- 使用Element UI的图标组件展示票务图标 -->
<i class="el-icon-tickets"></i>
<!-- 显示数据列表的文字说明 -->
<span>数据列表</span>
<!-- 使用Element UI的按钮组件设置按钮大小为mini样式类为btn-add绑定点击事件handleAdd用于添加广告操作 -->
<el-button size="mini" class="btn-add" @click="handleAdd()">广</el-button>
</el-card>
<!-- 定义一个用于放置表格的容器div -->
<div class="table-container">
<!-- 使用Element UI的el-table组件展示数据表格绑定数据为list设置宽度为100%监听选择改变事件handleSelectionChange根据listLoading控制加载状态设置表格边框 -->
<el-table ref="homeAdvertiseTable"
:data="list"
style="width: 100%;"
@selection-change="handleSelectionChange"
v-loading="listLoading" border>
<!-- 定义一个选择列设置宽度为60px内容居中对齐 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 定义一个编号列设置宽度为120px内容居中对齐通过插槽作用域展示对应行数据的id属性 -->
<el-table-column label="编号" width="120" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 定义一个广告名称列内容居中对齐通过插槽作用域展示对应行数据的name属性 -->
<el-table-column label="广告名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 定义一个广告位置列设置宽度为120px内容居中对齐通过插槽作用域展示经过formatType过滤器处理后的对应行数据的type属性 -->
<el-table-column label="广告位置" width="120" align="center">
<template slot-scope="scope">{{scope.row.type | formatType}}</template>
</el-table-column>
<!-- 定义一个广告图片列设置宽度为120px内容居中对齐通过插槽作用域展示对应行数据的pic属性对应的图片设置图片高度为80px -->
<el-table-column label="广告图片" width="120" align="center">
<template slot-scope="scope"><img style="height: 80px" :src="scope.row.pic"></template>
</el-table-column>
<!-- 定义一个时间列设置宽度为220px内容居中对齐通过插槽作用域展示对应行数据的开始时间和到期时间经过formatTime过滤器处理 -->
<el-table-column label="时间" width="220" align="center">
<template slot-scope="scope">
<p>开始时间{{scope.row.startTime | formatTime}}</p>
<p>到期时间{{scope.row.endTime | formatTime}}</p>
</template>
</el-table-column>
<!-- 定义一个上线/下线列设置宽度为120px内容居中对齐通过插槽作用域使用el-switch组件来切换对应行数据的status属性绑定相关的事件和属性值 -->
<el-table-column label="上线/下线" width="120" align="center">
<template slot-scope="scope">
<el-switch
@change="handleUpdateStatus(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.status">
</el-switch>
</template>
</el-table-column>
<!-- 定义一个点击次数列设置宽度为120px内容居中对齐通过插槽作用域展示对应行数据的clickCount属性 -->
<el-table-column label="点击次数" width="120" align="center">
<template slot-scope="scope">{{scope.row.clickCount}}</template>
</el-table-column>
<!-- 定义一个生成订单列设置宽度为120px内容居中对齐通过插槽作用域展示对应行数据的orderCount属性 -->
<el-table-column label="生成订单" width="120" align="center">
<template slot-scope="scope">{{scope.row.orderCount}}</template>
</el-table-column>
<!-- 定义一个操作列设置宽度为120px内容居中对齐通过插槽作用域使用按钮组件实现编辑和删除操作绑定对应的点击事件 -->
<el-table-column label="操作" width="120" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 定义一个用于批量操作的容器div -->
<div class="batch-operate-container">
<!-- 使用Element UI的el-select组件创建一个下拉选择框绑定v-model到operateType设置占位文本为批量操作 -->
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<!-- 循环遍历operates数组生成下拉选项每个选项根据对应的item设置keylabel和value属性 -->
<el-option
v-for="item in operates"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<!-- 使用Element UI的按钮组件设置距左侧20px的样式样式类为search-button绑定点击事件handleBatchOperate类型为主要按钮按钮大小为small用于确定批量操作 -->
<el-button
style="margin-left: 20px"
class="search-button"
@click="handleBatchOperate()"
type="primary"
size="small">
确定
</el-button>
</div>
<!-- 定义一个用于放置分页组件的容器div -->
<div class="pagination-container">
<!-- 使用Element UI的el-pagination组件实现分页功能设置背景色监听页面尺寸改变事件handleSizeChange当前页改变事件handleCurrentChange设置相关的布局页面尺寸可选页面尺寸当前页总数据量等属性 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
// @/api/homeAdvertisefetchListupdateStatusdeleteHomeAdvertise广
import {fetchList, updateStatus, deleteHomeAdvertise} from '@/api/homeAdvertise';
// @/utils/dateformatDate
import {formatDate} from '@/utils/date';
// 广广
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
name: null,
type: null,
endTime: null
};
// 广labelvalue广
const defaultTypeOptions = [
{
label: 'PC首页轮播',
value: 0
},
{
label: 'APP首页轮播',
value: 1
}
];
export default {
name: 'homeAdvertiseList',
data() {
return {
// defaultListQuery
listQuery: Object.assign({}, defaultListQuery),
// 广defaultTypeOptions广
typeOptions: Object.assign({}, defaultTypeOptions),
// 广
list: null,
//
total: null,
//
listLoading: false,
//
multipleSelection: [],
// labelvalue
operates: [
{
label: "删除",
value: 0
}
],
//
operateType: null
}
},
created() {
// getList广
this.getList();
},
filters: {
// formatType广
formatType(type) {
if (type === 1) {
return 'APP首页轮播';
} else {
return 'PC首页轮播';
}
},
// formatTime'N/A'
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
// listQuery
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
// 1getList广
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
// multipleSelection
handleSelectionChange(val) {
this.multipleSelection = val;
},
// 1getList广
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// getList广
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
// 线/线广
handleUpdateStatus(index, row) {
this.$confirm('是否要修改上线/下线状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id, {status: row.status}).then(response => {
this.getList();
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'success',
message: '已取消操作!'
});
this.getList();
});
},
// 广deleteHomeAdvertise广id
handleDelete(index, row) {
this.deleteHomeAdvertise(row.id);
},
// deleteHomeAdvertiseid
handleBatchOperate() {
if (this.multipleSelection < 1) {
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);
}
if (this.operateType === 0) {
//
this.deleteHomeAdvertise(ids);
} else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
}
},
// 广广/sms/addAdvertise
handleAdd() {
this.$router.push({path: '/sms/addAdvertise'})
},
// 广广/sms/updateAdvertise广id

@ -0,0 +1,32 @@
<template> 
<home-advertise-detail :isEdit="true"></home-advertise-detail>
</template>
<script>
import HomeAdvertiseDetail from './components/HomeAdvertiseDetail'
export default {
name: 'updateHomeAdvertise',
components: { HomeAdvertiseDetail }
}
</script>
<style></style>
#abc
<template>
<!-- 在当前模板中使用名为`home-advertise-detail`的自定义组件
并通过`:isEdit="true"`的方式向该组件传递一个名为`isEdit`的属性值为`true`
意味着当前是处于编辑状态可能用于控制组件内部不同的展示或操作逻辑 -->
<home-advertise-detail :isEdit="true"></home-advertise-detail>
</template>
<script>
// `./components/HomeAdvertiseDetail``HomeAdvertiseDetail`
// 使使
import HomeAdvertiseDetail from './components/HomeAdvertiseDetail'
export default {
name: 'updateHomeAdvertise',
// `components``HomeAdvertiseDetail`
// template使
components: { HomeAdvertiseDetail }
}
</script>
<style></style>

@ -0,0 +1,651 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="品牌名称:">
<el-input v-model="listQuery.brandName" class="input-width" placeholder="品牌名称"></el-input>
</el-form-item>
<el-form-item label="推荐状态:">
<el-select v-model="listQuery.recommendStatus" placeholder="全部" clearable class="input-width">
<el-option v-for="item in recommendOptions"
: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-button size="mini" class="btn-add" @click="handleSelectBrand()"></el-button>
</el-card>
<div class="table-container">
<el-table ref="homeBrandTable"
: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="120" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="品牌名称" align="center">
<template slot-scope="scope">{{scope.row.brandName}}</template>
</el-table-column>
<el-table-column label="是否推荐" width="200" align="center">
<template slot-scope="scope">
<el-switch
@change="handleRecommendStatusStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.recommendStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column label="排序" width="160" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<el-table-column label="状态" width="160" align="center">
<template slot-scope="scope">{{scope.row.recommendStatus | formatRecommendStatus}}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleEditSort(scope.$index, scope.row)">设置排序
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
: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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<el-dialog title="选择品牌" :visible.sync="selectDialogVisible" width="40%">
<el-input v-model="dialogData.listQuery.keyword"
style="width: 250px;margin-bottom: 20px"
size="small"
placeholder="品牌名称搜索">
<el-button slot="append" icon="el-icon-search" @click="handleSelectSearch()"></el-button>
</el-input>
<el-table :data="dialogData.list"
@selection-change="handleDialogSelectionChange" border>
<el-table-column type="selection" width="60" align="center"></el-table-column>
<el-table-column label="品牌名称"align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="相关" width="220" align="center">
<template slot-scope="scope">
商品<span class="color-main">{{scope.row.productCount}}</span>
评价<span class="color-main">{{scope.row.productCommentCount}}</span>
</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
background
@size-change="handleDialogSizeChange"
@current-change="handleDialogCurrentChange"
layout="prev, pager, next"
:current-page.sync="dialogData.listQuery.pageNum"
:page-size="dialogData.listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="dialogData.total">
</el-pagination>
</div>
<div style="clear: both;"></div>
<div slot="footer">
<el-button size="small" @click="selectDialogVisible = false"> </el-button>
<el-button size="small" type="primary" @click="handleSelectDialogConfirm()"> </el-button>
</div>
</el-dialog>
<el-dialog title="设置排序"
:visible.sync="sortDialogVisible"
width="40%">
<el-form :model="sortDialogData"
label-width="150px">
<el-form-item label="排序:">
<el-input v-model="sortDialogData.sort" style="width: 200px"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="sortDialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleUpdateSort" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,updateRecommendStatus,deleteHomeBrand,createHomeBrand,updateHomeBrandSort} from '@/api/homeBrand';
import {fetchList as fetchBrandList} from '@/api/brand';
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
brandName: null,
recommendStatus: null
};
const defaultRecommendOptions = [
{
label: '未推荐',
value: 0
},
{
label: '推荐中',
value: 1
}
];
export default {
name: 'homeBrandList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
recommendOptions: Object.assign({}, defaultRecommendOptions),
list: null,
total: null,
listLoading: false,
multipleSelection: [],
operates: [
{
label: "设为推荐",
value: 0
},
{
label: "取消推荐",
value: 1
},
{
label: "删除",
value: 2
}
],
operateType: null,
selectDialogVisible:false,
dialogData:{
list: null,
total: null,
multipleSelection:[],
listQuery:{
keyword: null,
showStatus:1,
pageNum: 1,
pageSize: 5
}
},
sortDialogVisible:false,
sortDialogData:{sort:0,id:null}
}
},
created() {
this.getList();
},
filters:{
formatRecommendStatus(status){
if(status===1){
return '推荐中';
}else{
return '未推荐';
}
}
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSelectionChange(val){
this.multipleSelection = val;
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleRecommendStatusStatusChange(index,row){
this.updateRecommendStatusStatus(row.id,row.recommendStatus);
},
handleDelete(index,row){
this.deleteBrand(row.id);
},
handleBatchOperate(){
if (this.multipleSelection < 1) {
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);
}
if (this.operateType === 0) {
//
this.updateRecommendStatusStatus(ids,1);
} else if (this.operateType === 1) {
//
this.updateRecommendStatusStatus(ids,0);
} else if(this.operateType===2){
//
this.deleteBrand(ids);
}else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
return;
}
},
handleSelectBrand(){
this.selectDialogVisible=true;
this.getDialogList();
},
handleSelectSearch(){
this.getDialogList();
},
handleDialogSizeChange(val) {
this.dialogData.listQuery.pageNum = 1;
this.dialogData.listQuery.pageSize = val;
this.getDialogList();
},
handleDialogCurrentChange(val) {
this.dialogData.listQuery.pageNum = val;
this.getDialogList();
},
handleDialogSelectionChange(val){
this.dialogData.multipleSelection = val;
},
handleSelectDialogConfirm(){
if (this.dialogData.multipleSelection < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
let selectBrands = [];
for (let i = 0; i < this.dialogData.multipleSelection.length; i++) {
selectBrands.push({
brandId:this.dialogData.multipleSelection[i].id,
brandName:this.dialogData.multipleSelection[i].name
});
}
this.$confirm('使用要进行添加操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
createHomeBrand(selectBrands).then(response=>{
this.selectDialogVisible=false;
this.dialogData.multipleSelection=[];
this.getList();
this.$message({
type: 'success',
message: '添加成功!'
});
});
});
},
handleEditSort(index,row){
this.sortDialogVisible=true;
this.sortDialogData.sort=row.sort;
this.sortDialogData.id=row.id;
},
handleUpdateSort(){
this.$confirm('是否要修改排序?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateHomeBrandSort(this.sortDialogData).then(response=>{
this.sortDialogVisible=false;
this.getList();
this.$message({
type: 'success',
message: '删除成功!'
});
});
})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
})
},
updateRecommendStatusStatus(ids,status){
this.$confirm('是否要修改推荐状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
params.append("recommendStatus",status);
updateRecommendStatus(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'success',
message: '已取消操作!'
});
this.getList();
});
},
deleteBrand(ids){
this.$confirm('是否要删除该推荐?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
deleteHomeBrand(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '删成功!'
});
});
})
},
getDialogList(){
fetchBrandList(this.dialogData.listQuery).then(response=>{
this.dialogData.list=response.data.list;
this.dialogData.total=response.data.total;
})
}
}
}
</script>
<style></style>
#abc
<template>
<!-- 页面的根容器使用了app-container类名 -->
<div class="app-container">
<!-- 筛选搜索的卡片容器设置了阴影效果为无 -->
<el-card class="filter-container" shadow="never">
<div>
<!-- 搜索图标 -->
<i class="el-icon-search"></i>
<!-- 筛选搜索的文字提示 -->
<span>筛选搜索</span>
<!-- 查询搜索按钮设置了样式类型点击事件以及尺寸点击后调用handleSearchList方法 -->
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<!-- 重置按钮设置了样式点击事件以及尺寸点击后调用handleResetSearch方法 -->
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<!-- 内联表单绑定了数据模型listQuery设置了尺寸和标签宽度 -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 品牌名称的表单项 -->
<el-form-item label="品牌名称:">
<!-- 输入框双向绑定listQuery.brandName设置了类名和占位符 -->
<el-input v-model="listQuery.brandName" class="input-width" placeholder="品牌名称"></el-input>
</el-form-item>
<!-- 推荐状态的表单项 -->
<el-form-item label="推荐状态:">
<!-- 下拉选择框双向绑定listQuery.recommendStatus设置了占位符可清除以及类名 -->
<el-select v-model="listQuery.recommendStatus" placeholder="全部" clearable class="input-width">
<!-- 循环生成下拉选项根据recommendOptions数据来生成每个选项 -->
<el-option v-for="item in recommendOptions"
: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>
<!-- 选择品牌按钮设置了尺寸和点击事件点击后调用handleSelectBrand方法 -->
<el-button size="mini" class="btn-add" @click="handleSelectBrand()"></el-button>
</el-card>
<!-- 表格容器 -->
<div class="table-container">
<!-- el-table组件用于展示数据列表绑定了数据设置了宽度选择改变事件加载状态以及边框 -->
<el-table ref="homeBrandTable"
:data="list"
style="width: 100%;"
@selection-change="handleSelectionChange"
v-loading="listLoading" border>
<!-- 选择列设置了宽度和对齐方式 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 编号列设置了宽度和对齐方式通过插槽作用域展示对应行的id数据 -->
<el-table-column label="编号" width="120" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 品牌名称列设置了对齐方式通过插槽作用域展示对应行的brandName数据 -->
<el-table-column label="品牌名称" align="center">
<template slot-scope="scope">{{scope.row.brandName}}</template>
</el-table-column>
<!-- 是否推荐列设置了宽度和对齐方式通过插槽作用域展示开关组件来切换推荐状态绑定了相应的事件和数据 -->
<el-table-column label="是否推荐" width="200" align="center">
<template slot-scope="scope">
<el-switch
@change="handleRecommendStatusStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.recommendStatus">
</el-switch>
</template>
</el-table-column>
<!-- 排序列设置了宽度和对齐方式通过插槽作用域展示对应行的sort数据 -->
<el-table-column label="排序" width="160" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<!-- 状态列设置了宽度和对齐方式通过插槽作用域展示格式化后的推荐状态数据 -->
<el-table-column label="状态" width="160" align="center">
<template slot-scope="scope">{{scope.row.recommendStatus | formatRecommendStatus}}</template>
</el-table-column>
<!-- 操作列设置了宽度和对齐方式通过插槽作用域展示编辑排序和删除按钮并绑定了相应的点击事件 -->
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleEditSort(scope.$index, scope.row)">设置排序
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 批量操作容器 -->
<div class="batch-operate-container">
<!-- 下拉选择框用于选择批量操作类型双向绑定operateType -->
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<!-- 循环生成下拉选项根据operates数据来生成每个选项 -->
<el-option
v-for="item in operates"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<!-- 确定按钮设置了样式点击事件类型以及尺寸点击后调用handleBatchOperate方法 -->
<el-button
style="margin-left: 20px"
class="search-button"
@click="handleBatchOperate()"
type="primary"
size="small">
确定
</el-button>
</div>
<!-- 分页容器 -->
<div class="pagination-container">
<!-- el-pagination组件用于分页功能绑定了相关的事件设置了布局每页显示数量可选的每页显示数量范围当前页码以及总数据量等属性 -->
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<!-- 选择品牌的对话框组件设置了标题显示状态绑定以及宽度 -->
<el-dialog title="选择品牌" :visible.sync="selectDialogVisible" width="40%">
<!-- 输入框用于在对话框中搜索品牌双向绑定dialogData.listQuery.keyword设置了样式尺寸和占位符还有点击搜索按钮的事件 -->
<el-input v-model="dialogData.listQuery.keyword"
style="width: 250px;margin-bottom: 20px"
size="small"
placeholder="品牌名称搜索">
<el-button slot="append" icon="el-icon-search" @click="handleSelectSearch()"></el-button>
</el-input>
<!-- 对话框中的表格用于展示可选择的品牌数据绑定了数据设置了选择改变事件和边框 -->
<el-table :data="dialogData.list"
@selection-change="handleDialogSelectionChange" border>
<!-- 选择列设置了宽度和对齐方式 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 品牌名称列设置了对齐方式通过插槽作用域展示对应行的name数据 -->
<el-table-column label="品牌名称"align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 相关列设置了宽度和对齐方式通过插槽作用域展示商品数量和评价数量等相关信息 -->
<el-table-column label="相关" width="220" align="center">
<template slot-scope="scope">
商品<span class="color-main">{{scope.row.productCount}}</span>
评价<span class="color-main">{{scope.row.productCommentCount}}</span>
</template>
</el-table-column>
</el-table>
<!-- 对话框中的分页容器 -->
<div class="pagination-container">
<!-- el-pagination组件用于对话框内的分页功能绑定了相关的事件设置了布局当前页码每页显示数量以及总数据量等属性 -->
<el-pagination
background
@size-change="handleDialogSizeChange"
@current-change="handleDialogCurrentChange"
layout="prev, pager, next"
:current-page.sync="dialogData.listQuery.pageNum"
:page-size="dialogData.listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="dialogData.total">
</el-pagination>
</div>
<div style="clear: both;"></div>
<div slot="footer">
<!-- 取消按钮设置了尺寸和点击事件点击后关闭对话框 -->
<el-button size="small" @click="selectDialogVisible = false"> </el-button>
<!-- 确定按钮设置了尺寸类型和点击事件点击后调用handleSelectDialogConfirm方法 -->
<el-button size="small" type="primary" @click="handleSelectDialogConfirm()"> </el-button>
</div>
</el-dialog>
<!-- 设置排序的对话框组件设置了标题显示状态绑定以及宽度 -->
<el-dialog title="设置排序"
:visible.sync="sortDialogVisible"
width="40%">
<!-- 表单绑定了sortDialogData数据模型设置了标签宽度 -->
<el-form :model="sortDialogData"
label-width="150px">
<!-- 排序的表单项输入框双向绑定sortDialogData.sort设置了宽度 -->
<el-form-item label="排序:">
<el-input v-model="sortDialogData.sort" style="width: 200px"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<!-- 取消按钮设置了尺寸和点击事件点击后关闭对话框 -->
<el-button @click="sortDialogVisible = false" size="small"> </el-button>
<!-- 确定按钮设置了尺寸类型和点击事件点击后调用handleUpdateSort方法 -->
<el-button type="primary" @click="handleUpdateSort" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script></script>

@ -0,0 +1,29 @@
<template> 
<coupon-detail :isEdit="false"></coupon-detail>
</template>
<script>
import CouponDetail from './components/CouponDetail'
export default {
name: 'addCoupon',
components: { CouponDetail }
}
</script>
<style scoped>
</style>
#abc
<template>
<!-- 使用CouponDetail组件来展示添加优惠券的相关界面通过属性绑定将isEdit设置为false表示当前处于添加优惠券非编辑的状态 -->
<coupon-detail :isEdit="false"></coupon-detail>
</template>
<script>
// ./components/CouponDetailCouponDetailCouponDetailVue
import CouponDetail from './components/CouponDetail'
export default {
name: 'addCoupon',
// componentsCouponDetailtemplate使<coupon-detail>
components: { CouponDetail }
}
</script>

@ -0,0 +1,810 @@
<template> 
<el-card class="form-container" shadow="never">
<el-form :model="coupon"
:rules="rules"
ref="couponFrom"
label-width="150px"
size="small">
<el-form-item label="优惠券类型:">
<el-select v-model="coupon.type">
<el-option
v-for="type in typeOptions"
:key="type.value"
:label="type.label"
:value="type.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="优惠券名称:" prop="name">
<el-input v-model="coupon.name" class="input-width"></el-input>
</el-form-item>
<el-form-item label="适用平台:">
<el-select v-model="coupon.platform">
<el-option
v-for="item in platformOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="总发行量:" prop="publishCount">
<el-input v-model.number="coupon.publishCount" placeholder="只能输入正整数" class="input-width"></el-input>
</el-form-item>
<el-form-item label="面额:" prop="amount">
<el-input v-model.number="coupon.amount" placeholder="面值只能是数值限2位小数" class="input-width">
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="每人限领:">
<el-input v-model="coupon.perLimit" placeholder="只能输入正整数" class="input-width">
<template slot="append"></template>
</el-input>
</el-form-item>
<el-form-item label="使用门槛:" prop="minPoint">
<el-input v-model.number="coupon.minPoint" placeholder="只能输入正整数" class="input-width">
<template slot="prepend"></template>
<template slot="append">元可用</template>
</el-input>
</el-form-item>
<el-form-item label="领取日期:" prop="enableTime">
<el-date-picker type="date" placeholder="选择日期" v-model="coupon.enableTime" class="input-width"></el-date-picker>
</el-form-item>
<el-form-item label="有效期:">
<el-date-picker type="date" placeholder="选择日期" v-model="coupon.startTime" style="width: 150px"></el-date-picker>
<span style="margin-left: 20px;margin-right: 20px"></span>
<el-date-picker type="date" placeholder="选择日期" v-model="coupon.endTime" style="width: 150px"></el-date-picker>
</el-form-item>
<el-form-item label="可使用商品:">
<el-radio-group v-model="coupon.useType">
<el-radio-button :label="0">全场通用</el-radio-button>
<el-radio-button :label="1">指定分类</el-radio-button>
<el-radio-button :label="2">指定商品</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-show="coupon.useType===1">
<el-cascader
clearable
placeholder="请选择分类名称"
v-model="selectProductCate"
:options="productCateOptions">
</el-cascader>
<el-button @click="handleAddProductCategoryRelation()"></el-button>
<el-table ref="productCateRelationTable"
:data="coupon.productCategoryRelationList"
style="width: 100%;margin-top: 20px"
border>
<el-table-column label="分类名称" align="center">
<template slot-scope="scope">{{scope.row.parentCategoryName}}>{{scope.row.productCategoryName}}</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleDeleteProductCateRelation(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item v-show="coupon.useType===2">
<el-select
v-model="selectProduct"
filterable
remote
reserve-keyword
placeholder="商品名称/商品货号"
:remote-method="searchProductMethod"
:loading="selectProductLoading">
<el-option
v-for="item in selectProductOptions"
:key="item.productId"
:label="item.productName"
:value="item.productId">
<span style="float: left">{{ item.productName }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">NO.{{ item.productSn }}</span>
</el-option>
</el-select>
<el-button @click="handleAddProductRelation()"></el-button>
<el-table ref="productRelationTable"
:data="coupon.productRelationList"
style="width: 100%;margin-top: 20px"
border>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.productName}}</template>
</el-table-column>
<el-table-column label="货号" align="center" width="120" >
<template slot-scope="scope">NO.{{scope.row.productSn}}</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleDeleteProductRelation(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item label="备注:">
<el-input
class="input-width"
type="textarea"
:rows="5"
placeholder="请输入内容"
v-model="coupon.note">
</el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('couponFrom')"></el-button>
<el-button v-if="!isEdit" @click="resetForm('couponFrom')"></el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
import {createCoupon,getCoupon,updateCoupon} from '@/api/coupon';
import {fetchSimpleList as fetchProductList} from '@/api/product';
import {fetchListWithChildren} from '@/api/productCate'
const defaultCoupon = {
type: 0,
name: null,
platform: 0,
amount: null,
perLimit: 1,
minPoint: null,
startTime: null,
endTime: null,
useType: 0,
note: null,
publishCount: null,
productRelationList: [],
productCategoryRelationList: []
};
const defaultTypeOptions = [
{
label: '全场赠券',
value: 0
},
{
label: '会员赠券',
value: 1
},
{
label: '购物赠券',
value: 2
},
{
label: '注册赠券',
value: 3
}
];
const defaultPlatformOptions = [
{
label: '全平台',
value: 0
},
{
label: '移动平台',
value: 1
},
{
label: 'PC平台',
value: 2
}
];
export default {
name: 'CouponDetail',
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
coupon: Object.assign({}, defaultCoupon),
typeOptions: Object.assign({}, defaultTypeOptions),
platformOptions: Object.assign({}, defaultPlatformOptions),
rules: {
name: [
{required: true, message: '请输入优惠券名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
publishCount: [
{type: 'number',required: true, message: '只能输入正整数', trigger: 'blur'}
],
amount: [
{type: 'number',required: true,message: '面值只能是数值0.01-10000限2位小数',trigger: 'blur'}
],
minPoint: [
{type: 'number',required: true,message: '只能输入正整数',trigger: 'blur'}
]
},
selectProduct:null,
selectProductLoading: false,
selectProductOptions:[],
selectProductCate: null,
productCateOptions: []
}
},
created(){
if(this.isEdit){
getCoupon(this.$route.query.id).then(response=>{
this.coupon=response.data;
});
}
this.getProductCateList();
},
methods:{
onSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$confirm('是否提交数据', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if(this.isEdit){
updateCoupon(this.$route.query.id,this.coupon).then(response=>{
this.$refs[formName].resetFields();
this.$message({
message: '修改成功',
type: 'success',
duration:1000
});
this.$router.back();
});
}else{
createCoupon(this.coupon).then(response=>{
this.$refs[formName].resetFields();
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.coupon = Object.assign({},defaultCoupon);
},
searchProductMethod(query){
if (query !== '') {
this.loading = true;
fetchProductList({keyword:query}).then(response=>{
this.loading=false;
let productList = response.data;
this.selectProductOptions = [];
for(let i=0;i<productList.length;i++){
let item = productList[i];
this.selectProductOptions.push({productId:item.id,productName:item.name,productSn:item.productSn});
}
});
} else {
this.selectProductOptions = [];
}
},
handleAddProductRelation(){
if(this.selectProduct===null){
this.$message({
message: '请先选择商品',
type: 'warning'
});
return
}
this.coupon.productRelationList.push(this.getProductById(this.selectProduct));
this.selectProduct=null;
},
handleDeleteProductRelation(index,row){
this.coupon.productRelationList.splice(index,1);
},
handleAddProductCategoryRelation(){
if(this.selectProductCate===null||this.selectProductCate.length===0){
this.$message({
message: '请先选择商品分类',
type: 'warning'
});
return
}
this.coupon.productCategoryRelationList.push(this.getProductCateByIds(this.selectProductCate));
this.selectProductCate=[];
},
handleDeleteProductCateRelation(index,row){
this.coupon.productCategoryRelationList.splice(index,1);
},
getProductById(id){
for(let i=0;i<this.selectProductOptions.length;i++){
if(id===this.selectProductOptions[i].productId){
return this.selectProductOptions[i];
}
}
return null;
},
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});
}
});
},
getProductCateByIds(ids){
let name;
let parentName;
for (let i = 0; i < this.productCateOptions.length; i++) {
if (this.productCateOptions[i].value === ids[0]) {
parentName = this.productCateOptions[i].label;
for (let j = 0; j < this.productCateOptions[i].children.length; j++) {
if (this.productCateOptions[i].children[j].value === ids[1]) {
name = this.productCateOptions[i].children[j].label;
}
}
}
}
return {productCategoryId: ids[1], productCategoryName: name, parentCategoryName: parentName};
}
}
}
</script>
<style scoped>
.input-width {
width: 60%;
}
</style>
#abc
<template>
<!-- 使用el-card组件创建一个表单容器设置阴影效果为无 -->
<el-card class="form-container" shadow="never">
<!-- 创建一个el-form表单组件绑定数据模型为coupon设置验证规则表单引用标签宽度尺寸等属性 -->
<el-form :model="coupon"
:rules="rules"
ref="couponFrom"
label-width="150px"
size="small">
<!-- 优惠券类型选择的表单项 -->
<el-form-item label="优惠券类型:">
<!-- el-select组件用于选择优惠券类型双向绑定v-model到coupon.type -->
<el-select v-model="coupon.type">
<!-- 遍历typeOptions数组生成el-option选项每个选项的键标签值分别对应相应属性 -->
<el-option
v-for="type in typeOptions"
:key="type.value"
:label="type.label"
:value="type.value">
</el-option>
</el-select>
</el-form-item>
<!-- 优惠券名称输入的表单项设置了验证属性prop -->
<el-form-item label="优惠券名称:" prop="name">
<!-- el-input组件用于输入优惠券名称双向绑定v-model到coupon.name -->
<el-input v-model="coupon.name" class="input-width"></el-input>
</el-form-item>
<!-- 适用平台选择的表单项 -->
<el-form-item label="适用平台:">
<el-select v-model="coupon.platform">
<el-option
v-for="item in platformOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 总发行量输入的表单项设置了验证属性prop限制只能输入正整数 -->
<el-form-item label="总发行量:" prop="publishCount">
<el-input v-model.number="coupon.publishCount" placeholder="只能输入正整数" class="input-width"></el-input>
</el-form-item>
<!-- 面额输入的表单项设置了验证属性prop限制只能输入数值且保留2位小数 -->
<el-form-item label="面额:" prop="amount">
<el-input v-model.number="coupon.amount" placeholder="面值只能是数值限2位小数" class="input-width">
<!-- 在输入框后面添加固定文本 -->
<template slot="append"></template>
</el-input>
</el-form-item>
<!-- 每人限领输入的表单项限制只能输入正整数 -->
<el-form-item label="每人限领:">
<el-input v-model="coupon.perLimit" placeholder="只能输入正整数" class="input-width">
<template slot="append"></template>
</el-input>
</el-form-item>
<!-- 使用门槛输入的表单项设置了验证属性prop限制只能输入正整数 -->
<el-form-item label="使用门槛:" prop="minPoint">
<el-input v-model.number="coupon.minPoint" placeholder="只能输入正整数" class="input-width">
<!-- 在输入框前面添加固定文本 -->
<template slot="prepend"></template>
<!-- 在输入框后面添加固定文本元可用 -->
<template slot="append">元可用</template>
</el-input>
</el-form-item>
<!-- 领取日期选择的表单项设置了验证属性prop -->
<el-form-item label="领取日期:" prop="enableTime">
<!-- el-date-picker组件用于选择日期类型为date双向绑定v-model到coupon.enableTime -->
<el-date-picker type="date" placeholder="选择日期" v-model="coupon.enableTime" class="input-width"></el-date-picker>
</el-form-item>
<!-- 有效期选择的表单项通过两个el-date-picker组件分别选择开始和结束日期 -->
<el-form-item label="有效期:">
<el-date-picker type="date" placeholder="选择日期" v-model="coupon.startTime" style="width: 150px"></el-date-picker>
<span style="margin-left: 20px;margin-right: 20px"></span>
<el-date-picker type="date" placeholder="选择日期" v-model="coupon.endTime" style="width: 150px"></el-date-picker>
</el-form-item>
<!-- 可使用商品选择的表单项通过el-radio-group和el-radio-button组件实现单选 -->
<el-form-item label="可使用商品:">
<el-radio-group v-model="coupon.useType">
<el-radio-button :label="0">全场通用</el-radio-button>
<el-radio-button :label="1">指定分类</el-radio-button>
<el-radio-button :label="2">指定商品</el-radio-button>
</el-radio-group>
</el-form-item>
<!-- 当选择指定分类coupon.useType === 1时显示的表单项 -->
<el-form-item v-show="coupon.useType===1">
<!-- el-cascader组件用于选择分类支持清空占位提示等功能双向绑定v-model到selectProductCate -->
<el-cascader
clearable
placeholder="请选择分类名称"
v-model="selectProductCate"
:options="productCateOptions">
</el-cascader>
<!-- 点击按钮触发handleAddProductCategoryRelation方法添加分类关系 -->
<el-button @click="handleAddProductCategoryRelation()"></el-button>
<!-- el-table组件用于展示已添加的分类关系列表 -->
<el-table ref="productCateRelationTable"
:data="coupon.productCategoryRelationList"
style="width: 100%;margin-top: 20px"
border>
<el-table-column label="分类名称" align="center">
<!-- 自定义表格单元格内容展示展示分类名称的层级结构 -->
<template slot-scope="scope">{{scope.row.parentCategoryName}}>{{scope.row.productCategoryName}}</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<!-- 点击按钮触发handleDeleteProductCateRelation方法删除对应分类关系 -->
<el-button size="mini"
type="text"
@click="handleDeleteProductCateRelation(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 当选择指定商品coupon.useType === 2时显示的表单项 -->
<el-form-item v-show="coupon.useType===2">
<!-- el-select组件用于选择商品支持过滤远程搜索保留关键字等功能双向绑定v-model到selectProduct -->
<el-select
v-model="selectProduct"
filterable
remote
reserve-keyword
placeholder="商品名称/商品货号"
:remote-method="searchProductMethod"
:loading="selectProductLoading">
<el-option
v-for="item in selectProductOptions"
:key="item.productId"
:label="item.productName"
:value="item.productId">
<span style="float: left">{{ item.productName }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">NO.{{ item.productSn }}</span>
</el-option>
</el-select>
<el-button @click="handleAddProductRelation()"></el-button>
<el-table ref="productRelationTable"
:data="coupon.productRelationList"
style="width: 100%;margin-top: 20px"
border>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.productName}}</template>
</el-table-column>
<el-table-column label="货号" align="center" width="120" >
<template slot-scope="scope">NO.{{scope.row.productSn}}</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleDeleteProductRelation(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 备注输入的表单项使用textarea类型的el-input组件 -->
<el-form-item label="备注:">
<el-input
class="input-width"
type="textarea"
:rows="5"
placeholder="请输入内容"
v-model="coupon.note">
</el-input>
</el-form-item>
<!-- 提交和重置按钮的表单项 -->
<el-form-item>
<!-- 点击提交按钮触发onSubmit方法提交表单数据 -->
<el-button type="primary" @click="onSubmit('couponFrom')"></el-button>
<!-- 当不是编辑状态!isEdit点击重置按钮触发resetForm方法重置表单 -->
<el-button v-if="!isEdit" @click="resetForm('couponFrom')"></el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
// '@/api/coupon'createCoupongetCouponupdateCoupon
import {createCoupon, getCoupon, updateCoupon} from '@/api/coupon';
// '@/api/product'fetchProductList
import {fetchSimpleList as fetchProductList} from '@/api/product';
// '@/api/productCate'便
import {fetchListWithChildren} from '@/api/productCate';
//
const defaultCoupon = {
type: 0, // 0typeOptions
name: null, //
platform: 0, // 0platformOptions
amount: null, //
perLimit: 1, // 1
minPoint: null, // 使
startTime: null, //
endTime: null, //
useType: 0, // 使0
note: null, //
publishCount: null, //
productRelationList: [], //
productCategoryRelationList: [] //
};
// labelvalue
const defaultTypeOptions = [
{
label: '全场赠券',
value: 0
},
{
label: '会员赠券',
value: 1
},
{
label: '购物赠券',
value: 2
},
{
label: '注册赠券',
value: 3
}
];
// PC
const defaultPlatformOptions = [
{
label: '全平台',
value: 0
},
{
label: '移动平台',
value: 1
},
{
label: 'PC平台',
value: 2
}
];
export default {
name: 'CouponDetail',
props: {
// isEditfalse
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
// defaultCoupon便
coupon: Object.assign({}, defaultCoupon),
// defaultTypeOptions
typeOptions: Object.assign({}, defaultTypeOptions),
// defaultPlatformOptions
platformOptions: Object.assign({}, defaultPlatformOptions),
//
rules: {
name: [
{required: true, message: '请输入优惠券名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
publishCount: [
{type: 'number', required: true, message: '只能输入正整数', trigger: 'blur'}
],
amount: [
{type: 'number', required: true, message: '面值只能是数值0.01-10000限2位小数', trigger: 'blur'}
],
minPoint: [
{type: 'number', required: true, message: '只能输入正整数', trigger: 'blur'}
]
},
// null便
selectProduct: null,
// falsetruefalse
selectProductLoading: false,
//
selectProductOptions: [],
// null
selectProductCate: null,
//
productCateOptions: []
}
},
created() {
// isEditisEdittruegetCouponIDthis.$route.query.idcoupon
if (this.isEdit) {
getCoupon(this.$route.query.id).then(response => {
this.coupon = response.data;
});
}
// getProductCateListproductCateOptions便
this.getProductCateList();
},
methods: {
// formNameisEdit
onSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$confirm('是否提交数据', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
// updateCouponIDcoupon
updateCoupon(this.$route.query.id, this.coupon).then(response => {
this.$refs[formName].resetFields();
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
this.$router.back();
});
} else {
// createCouponcoupon
createCoupon(this.coupon).then(response => {
this.$refs[formName].resetFields();
this.$message({
message: '提交成功',
type: 'success',
duration: 1000
});
this.$router.back();
});
}
});
} else {
// false
this.$message({
message: '验证失败',
type: 'error',
duration: 1000
});
return false;
}
});
},
// formNameresetFieldscoupondefaultCoupon
resetForm(formName) {
this.$refs[formName].resetFields();
this.coupon = Object.assign({}, defaultCoupon);
},
// querytruefetchProductListfalseselectProductOptions便selectProductOptions
searchProductMethod(query) {
if (query!== '') {
this.loading = true;
fetchProductList({keyword: query}).then(response => {
this.loading = false;
let productList = response.data;
this.selectProductOptions = [];
for (let i = 0; i < productList.length; i++) {
let item = productList[i];
this.selectProductOptions.push({productId: item.id, productName: item.name, productSn: item.productSn});
}
});
} else {
this.selectProductOptions = [];
}
},
// selectProductnullgetProductByIdcouponproductRelationListselectProductnull
handleAddProductRelation() {
if (this.selectProduct === null) {
this.$message({
message: '请先选择商品',
type: 'warning'
});
return;
}
this.coupon.productRelationList.push(this.getProductById(this.selectProduct));
this.selectProduct = null;
},
// productRelationListindexrowspliceproductRelationList
handleDeleteProductRelation(index, row) {
this.coupon.productRelationList.splice(index, 1);
},
// selectProductCatenull0getProductCateByIdscouponproductCategoryRelationListselectProductCate
handleAddProductCategoryRelation() {
if (this.selectProductCate === null || this.selectProductCate.length === 0) {
this.$message({
message: '请先选择商品分类',
type: 'warning'
});
return;
}
this.coupon.productCategoryRelationList.push(this.getProductCateByIds(this.selectProductCate));
this.selectProductCate = [];
},
// productCategoryRelationListindexrowsplice
handleDeleteProductCateRelation(index, row) {
this.coupon.productCategoryRelationList.splice(index, 1);
},
// IDselectProductOptionsIDnullID
getProductById(id) {
for (let i = 0; i < this.selectProductOptions.length; i++) {
if (id === this.selectProductOptions[i].productId) {
return this.selectProductOptions[i];
}
}
return null;
},
// fetchListWithChildrenproductCateOptions
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});
}
});
},
// IDproductCateOptionsID
getProductCateByIds(ids) {
let name;
let parentName;
for (let i = 0; i < this.productCateOptions.length; i++) {
if (this.productCateOptions[i].value === ids[0]) {
parentName = this.productCateOptions[i].label;
for (let j = 0; j < this.productCateOptions[i].children.length; j++) {
if (this.productCateOptions[i].children[j].value === ids[1]) {
name = this.productCateOptions[i].children[j].label;
}
}
}
}
return {productCategoryId: ids[1], productCategoryName: name, parentCategoryName: parentName};
}
}
}
</script>

@ -0,0 +1,642 @@
<template> 
<div class="app-container">
<div class="table-layout">
<el-row>
<el-col :span="4" class="table-cell-title">名称</el-col>
<el-col :span="4" class="table-cell-title">优惠券类型</el-col>
<el-col :span="4" class="table-cell-title">可使用商品</el-col>
<el-col :span="4" class="table-cell-title">使用门槛</el-col>
<el-col :span="4" class="table-cell-title">面值</el-col>
<el-col :span="4" class="table-cell-title">状态</el-col>
</el-row>
<el-row>
<el-col :span="4" class="table-cell">{{coupon.name}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.type | formatType}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.useType | formatUseType}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.minPoint}}元可用</el-col>
<el-col :span="4" class="table-cell">{{coupon.amount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.endTime | formatStatus}}</el-col>
</el-row>
<el-row>
<el-col :span="4" class="table-cell-title">有效期</el-col>
<el-col :span="4" class="table-cell-title">总发行量</el-col>
<el-col :span="4" class="table-cell-title">已领取</el-col>
<el-col :span="4" class="table-cell-title">待领取</el-col>
<el-col :span="4" class="table-cell-title">已使用</el-col>
<el-col :span="4" class="table-cell-title">未使用</el-col>
</el-row>
<el-row>
<el-col :span="4" class="table-cell" style="font-size: 13px">
{{coupon.startTime|formatDate}}{{coupon.endTime|formatDate}}
</el-col>
<el-col :span="4" class="table-cell">{{coupon.publishCount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.receiveCount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.publishCount-coupon.receiveCount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.useCount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.publishCount-coupon.useCount}}</el-col>
</el-row>
</div>
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="使用状态:">
<el-select v-model="listQuery.useStatus" placeholder="全部" clearable class="input-width">
<el-option v-for="item in useTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="订单编号:">
<el-input v-model="listQuery.orderSn" class="input-width" placeholder="订单编号"></el-input>
</el-form-item>
</el-form>
</div>
</el-card>
<div class="table-container">
<el-table ref="couponHistoryTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column label="优惠码" width="160" align="center">
<template slot-scope="scope">{{scope.row.couponCode}}</template>
</el-table-column>
<el-table-column label="领取会员" width="140" align="center">
<template slot-scope="scope">{{scope.row.memberNickname}}</template>
</el-table-column>
<el-table-column label="领取方式" width="100" align="center">
<template slot-scope="scope">{{scope.row.getType | formatGetType}}</template>
</el-table-column>
<el-table-column label="领取时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatTime}}</template>
</el-table-column>
<el-table-column label="当前状态" width="140" align="center">
<template slot-scope="scope">{{scope.row.useStatus | formatCouponHistoryUseType}}</template>
</el-table-column>
<el-table-column label="使用时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.useTime | formatTime}}</template>
</el-table-column>
<el-table-column label="订单编号" align="center">
<template slot-scope="scope">{{scope.row.orderSn===null?'N/A':scope.row.orderSn}}</template>
</el-table-column>
</el-table>
</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>
</div>
</template>
<script>
import {formatDate} from '@/utils/date';
import {getCoupon} from '@/api/coupon';
import {fetchList as fetchCouponHistoryList} from '@/api/couponHistory';
const defaultTypeOptions = [
{
label: '全场赠券',
value: 0
},
{
label: '会员赠券',
value: 1
},
{
label: '购物赠券',
value: 2
},
{
label: '注册赠券',
value: 3
}
];
const defaultListQuery = {
pageNum: 1,
pageSize: 10,
useStatus: null,
orderSn: null,
couponId: null
};
const defaultUseTypeOptions= [
{
label: "未使用",
value: 0
},
{
label: "已使用",
value: 1
},
{
label: "已过期",
value: 2
}
];
export default {
name: 'couponHistoryList',
data() {
return {
coupon: {},
listQuery: Object.assign({}, defaultListQuery),
useTypeOptions:Object.assign({},defaultUseTypeOptions),
list:null,
total:null,
listLoading:false
}
},
created() {
getCoupon(this.$route.query.id).then(response => {
this.coupon = response.data;
});
this.listQuery.couponId=this.$route.query.id;
this.getList();
},
filters: {
formatType(type) {
for (let i = 0; i < defaultTypeOptions.length; i++) {
if (type === defaultTypeOptions[i].value) {
return defaultTypeOptions[i].label;
}
}
return '';
},
formatUseType(useType) {
if (useType === 0) {
return '全场通用';
} else if (useType === 1) {
return '指定分类';
} else {
return '指定商品';
}
},
formatPlatform(platform) {
if (platform === 1) {
return '移动平台';
} else if (platform === 2) {
return 'PC平台';
} else {
return '全平台';
}
},
formatDate(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd')
},
formatStatus(endTime) {
let now = new Date().getTime();
if (endTime > now) {
return '未过期'
} else {
return '已过期';
}
},
formatGetType(type) {
if(type===1){
return '主动获取';
}else{
return '后台赠送';
}
},
formatCouponHistoryUseType(useType) {
if (useType === 0) {
return '未使用';
} else if (useType === 1) {
return '已使用';
} else {
return '已过期';
}
},
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
getList(){
this.listLoading=true;
fetchCouponHistoryList(this.listQuery).then(response=>{
this.listLoading=false;
this.list=response.data.list;
this.total=response.data.total;
});
},
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
this.listQuery.couponId=this.$route.query.id;
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
}
}
}
</script>
<style scoped>
.app-container {
width: 80%;
margin: 20px auto;
}
.filter-container {
margin-top: 20px;
}
.table-layout {
margin-top: 20px;
border-left: 1px solid #DCDFE6;
border-top: 1px solid #DCDFE6;
}
.table-cell {
height: 60px;
line-height: 40px;
border-right: 1px solid #DCDFE6;
border-bottom: 1px solid #DCDFE6;
padding: 10px;
font-size: 14px;
color: #606266;
text-align: center;
overflow: hidden;
}
.table-cell-title {
border-right: 1px solid #DCDFE6;
border-bottom: 1px solid #DCDFE6;
padding: 10px;
background: #F2F6FC;
text-align: center;
font-size: 14px;
color: #303133;
}
</style>
#abc
<template>
<!-- 创建一个类名为app-container的div作为整个页面的容器用于布局和包裹内部的各个模块 -->
<div class="app-container">
<!-- 创建一个用于展示优惠券相关信息表格布局的div -->
<div class="table-layout">
<!-- 使用el-row组件创建行el-col组件用于在行内划分列这里定义了一行展示优惠券不同属性的标题列 -->
<el-row>
<el-col :span="4" class="table-cell-title">名称</el-col>
<el-col :span="4" class="table-cell-title">优惠券类型</el-col>
<el-col :span="4" class="table-cell-title">可使用商品</el-col>
<el-col :span="4" class="table-cell-title">使用门槛</el-col>
<el-col :span="4" class="table-cell-title">面值</el-col>
<el-col :span="4" class="table-cell-title">状态</el-col>
</el-row>
<!-- 又一行el-row用于展示具体优惠券的各项属性值通过双花括号插值表达式绑定对应的数据部分属性使用了过滤器 | formatType等来格式化显示内容 -->
<el-row>
<el-col :span="4" class="table-cell">{{coupon.name}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.type | formatType}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.useType | formatUseType}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.minPoint}}元可用</el-col>
<el-col :span="4" class="table-cell">{{coupon.amount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.endTime | formatStatus}}</el-col>
</el-row>
<!-- 再次创建一行el-row用于展示优惠券其他属性的标题列 -->
<el-row>
<el-col :span="4" class="table-cell-title">有效期</el-col>
<el-col :span="4" class="table-cell-title">总发行量</el-col>
<el-col :span="4" class="table-cell-title">已领取</el-col>
<el-col :span="4" class="table-cell-title">待领取</el-col>
<el-col :span="4" class="table-cell-title">已使用</el-col>
<el-col :span="4" class="table-cell-title">未使用</el-col>
</el-row>
<!-- 对应上一行标题列的具体属性值展示行通过计算表达式等方式来展示相应的数据如计算待领取数量等 -->
<el-row>
<el-col :span="4" class="table-cell" style="font-size: 13px">
{{coupon.startTime|formatDate}}{{coupon.endTime|formatDate}}
</el-col>
<el-col :span="4" class="table-cell">{{coupon.publishCount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.receiveCount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.publishCount - coupon.receiveCount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.useCount}}</el-col>
<el-col :span="4" class="table-cell">{{coupon.publishCount - coupon.useCount}}</el-col>
</el-row>
</div>
<!-- 使用el-card组件创建一个筛选搜索的容器设置阴影效果为无 -->
<el-card class="filter-container" shadow="never">
<div>
<!-- 使用一个图标元素这里假设是element-ui的图标类表示搜索图标 -->
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<!-- 点击按钮触发handleSearchList方法进行查询搜索操作按钮设置为主要样式primary小尺寸small并靠右浮动float:right -->
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<!-- 点击按钮触发handleResetSearch方法重置筛选搜索条件按钮靠右浮动且设置了右边距margin-right: 15px小尺寸small -->
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<!-- 创建一个内联表单inline为true绑定数据模型为listQuery设置表单尺寸为小small标签宽度为140px -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单中使用状态选择的表单项 -->
<el-form-item label="使用状态:">
<el-select v-model="listQuery.useStatus" placeholder="全部" clearable class="input-width">
<el-option v-for="item in useTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 表单中订单编号输入的表单项通过v-model双向绑定数据到listQuery.orderSn -->
<el-form-item label="订单编号:">
<el-input v-model="listQuery.orderSn" class="input-width" placeholder="订单编号"></el-input>
</el-form-item>
</el-form>
</div>
</el-card>
<!-- 创建一个用于展示优惠券历史记录表格的容器div -->
<div class="table-container">
<!-- 使用el-table组件展示优惠券历史记录数据绑定数据来源为list设置宽度为100%通过v-loading指令绑定加载状态为listLoading显示表格边框 -->
<el-table ref="couponHistoryTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<!-- 定义表格列优惠码设置宽度为160px内容居中对齐 -->
<el-table-column label="优惠码" width="160" align="center">
<template slot-scope="scope">{{scope.row.couponCode}}</template>
</el-table-column>
<!-- 定义表格列领取会员宽度为140px内容居中对齐 -->
<el-table-column label="领取会员" width="140" align="center">
<template slot-scope="scope">{{scope.row.memberNickname}}</template>
</el-table-column>
<!-- 定义表格列领取方式宽度为100px内容居中对齐使用了formatGetType过滤器格式化显示内容 -->
<el-table-column label="领取方式" width="100" align="center">
<template slot-scope="scope">{{scope.row.getType | formatGetType}}</template>
</el-table-column>
<!-- 定义表格列领取时间宽度为160px内容居中对齐使用了formatTime过滤器格式化显示内容 -->
<el-table-column label="领取时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatTime}}</template>
</el-table-column>
<!-- 定义表格列当前状态宽度为140px内容居中对齐使用了formatCouponHistoryUseType过滤器格式化显示内容 -->
<el-table-column label="当前状态" width="140" align="center">
<template slot-scope="scope">{{scope.row.useStatus | formatCouponHistoryUseType}}</template>
</el-table-column>
<!-- 定义表格列使用时间宽度为160px内容居中对齐使用了formatTime过滤器格式化显示内容 -->
<el-table-column label="使用时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.useTime | formatTime}}</template>
</el-table-column>
<!-- 定义表格列订单编号内容居中对齐当值为null时显示N/A -->
<el-table-column label="订单编号" align="center">
<template slot-scope="scope">{{scope.row.orderSn === null? 'N/A' : scope.row.orderSn}}</template>
</el-table-column>
</el-table>
</div>
<!-- 创建一个用于放置分页组件的容器div -->
<div class="pagination-container">
<!-- 使用el-pagination组件实现分页功能设置背景色background绑定页面尺寸改变size-change和当前页改变current-change的事件处理方法配置分页布局当前页每页数量可选页面尺寸列表以及总记录数等属性 -->
<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>
</div>
</template>
<script>
// '@/utils/date'formatDate使
import {formatDate} from '@/utils/date';
// '@/api/coupon'getCoupon
import {getCoupon} from '@/api/coupon';
// '@/api/couponHistory'fetchListfetchCouponHistoryList
import {fetchList as fetchCouponHistoryList} from '@/api/couponHistory';
// labelvalue
const defaultTypeOptions = [
{
label: '全场赠券',
value: 0
},
{
label: '会员赠券',
value: 1
},
{
label: '购物赠券',
value: 2
},
{
label: '注册赠券',
value: 3
}
];
//
const defaultListQuery = {
pageNum: 1, // 1
pageSize: 10, // 10
useStatus: null, // 使
orderSn: null, //
couponId: null // ID
};
// 使labelvalue使
const defaultUseTypeOptions = [
{
label: "未使用",
value: 0
},
{
label: "已使用",
value: 1
},
{
label: "已过期",
value: 2
}
];
export default {
name: 'couponHistoryList',
data() {
return {
//
coupon: {},
// Object.assigndefaultListQuery便
listQuery: Object.assign({}, defaultListQuery),
// 使defaultUseTypeOptions使
useTypeOptions: Object.assign({}, defaultUseTypeOptions),
// null
list: null,
// null
total: null,
// falsetruefalse
listLoading: false
}
},
created() {
// getCouponthis.$route.query.idIDcoupon便使
getCoupon(this.$route.query.id).then(response => {
this.coupon = response.data;
});
// couponIdID
this.listQuery.couponId = this.$route.query.id;
// getList
this.getList();
},
filters: {
// formatTypetypedefaultTypeOptions
formatType(type) {
for (let i = 0; i < defaultTypeOptions.length; i++) {
if (type === defaultTypeOptions[i].value) {
return defaultTypeOptions[i].label;
}
}
return '';
},
// formatUseType使useType0便使
formatUseType(useType) {
if (useType === 0) {
return '全场通用';
} else if (useType === 1) {
return '指定分类';
} else {
return '指定商品';
}
},
// formatPlatformplatform1
formatPlatform(platform) {
if (platform === 1) {
return '移动平台';
} else if (platform === 2) {
return 'PC平台';
} else {
return '全平台';
}
},
// formatDatenull'N/A'formatDate'yyyy-MM-dd'使
formatDate(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd')
},
// formatStatusendTimenew Date().getTime()
formatStatus(endTime) {
let now = new Date().getTime();
if (endTime > now) {
return '未过期'
} else {
return '已过期';
}
},
// formatGetTypetypetype1
formatGetType(type) {
if (type === 1) {
return '主动获取';
} else {
return '后台赠送';
}
},
// formatCouponHistoryUseType使useType0使使便使
formatCouponHistoryUseType(useType) {
if (useType === 0) {
return '未使用';
} else if (useType === 1) {
return '已使用';
} else {
return '已过期';
}
},
// formatTimeformatDate'yyyy-MM-dd hh:mm:ss'null'N/A'
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
// listLoadingtruefetchCouponHistoryListlistQuerylistLoadingfalselisttotal
getList() {
this.listLoading = true;
fetchCouponHistoryList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
// Object.assigndefaultListQuerycouponId便
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
this.listQuery.couponId = this.$route.query.id;
},
// 1getListuseStatus
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
// 1valgetList
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// valgetList
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
}
}
}
</script>

@ -0,0 +1,545 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="优惠券名称:">
<el-input v-model="listQuery.name" class="input-width" placeholder="优惠券名称"></el-input>
</el-form-item>
<el-form-item label="优惠券类型:">
<el-select v-model="listQuery.type" placeholder="全部" clearable class="input-width">
<el-option v-for="item in typeOptions"
: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-button size="mini" class="btn-add" @click="handleAdd()"></el-button>
</el-card>
<div class="table-container">
<el-table ref="couponTable"
: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="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="优惠劵名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="优惠券类型" width="100" align="center">
<template slot-scope="scope">{{scope.row.type | formatType}}</template>
</el-table-column>
<el-table-column label="可使用商品" width="100" align="center">
<template slot-scope="scope">{{scope.row.useType | formatUseType}}</template>
</el-table-column>
<el-table-column label="使用门槛" width="140" align="center">
<template slot-scope="scope">{{scope.row.minPoint}}元可用</template>
</el-table-column>
<el-table-column label="面值" width="100" align="center">
<template slot-scope="scope">{{scope.row.amount}}</template>
</el-table-column>
<el-table-column label="适用平台" width="100" align="center">
<template slot-scope="scope">{{scope.row.platform | formatPlatform}}</template>
</el-table-column>
<el-table-column label="有效期" width="180" align="center">
<template slot-scope="scope">{{scope.row.startTime|formatDate}}{{scope.row.endTime|formatDate}}</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">{{scope.row.endTime | formatStatus}}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleView(scope.$index, scope.row)">查看</el-button>
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">
编辑</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</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>
</div>
</template>
<script>
import {fetchList,deleteCoupon} from '@/api/coupon';
import {formatDate} from '@/utils/date';
const defaultListQuery = {
pageNum: 1,
pageSize: 10,
name: null,
type: null
};
const defaultTypeOptions=[
{
label: '全场赠券',
value: 0
},
{
label: '会员赠券',
value: 1
},
{
label: '购物赠券',
value: 2
},
{
label: '注册赠券',
value: 3
}
];
export default {
name:'couponList',
data() {
return {
listQuery:Object.assign({},defaultListQuery),
typeOptions:Object.assign({},defaultTypeOptions),
list:null,
total:null,
listLoading:false,
multipleSelection:[]
}
},
created(){
this.getList();
},
filters:{
formatType(type){
for(let i=0;i<defaultTypeOptions.length;i++){
if(type===defaultTypeOptions[i].value){
return defaultTypeOptions[i].label;
}
}
return '';
},
formatUseType(useType){
if(useType===0){
return '全场通用';
}else if(useType===1){
return '指定分类';
}else{
return '指定商品';
}
},
formatPlatform(platform){
if(platform===1){
return '移动平台';
}else if(platform===2){
return 'PC平台';
}else{
return '全平台';
}
},
formatDate(time){
if(time==null||time===''){
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd')
},
formatStatus(endTime){
let now = new Date().getTime();
let endDate = new Date(endTime);
if(endDate>now){
return '未过期'
}else{
return '已过期';
}
}
},
methods:{
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSelectionChange(val){
this.multipleSelection = val;
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleAdd(){
this.$router.push({path: '/sms/addCoupon'})
},
handleView(index, row) {
this.$router.push({path: '/sms/couponHistory', query: {id: row.id}})
},
handleUpdate(index, row) {
this.$router.push({path: '/sms/updateCoupon', query: {id: row.id}})
},
handleDelete(index, row) {
this.$confirm('是否进行删除操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteCoupon(row.id).then(response=>{
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
})
},
getList(){
this.listLoading=true;
fetchList(this.listQuery).then(response=>{
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
}
}
}
</script>
<style scoped>
.input-width {
width: 203px;
}
</style>
#abc
<template>
<!-- 创建一个类名为app-container的div作为整个页面的外层容器用于整体布局 -->
<div class="app-container">
<!-- 使用el-card组件创建一个筛选搜索的容器设置阴影效果为无 -->
<el-card class="filter-container" shadow="never">
<div>
<!-- 使用element-ui的图标元素el-icon-search表示搜索图标 -->
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<!-- el-button组件用于创建查询搜索按钮设置按钮靠右浮动float:right样式为主要按钮type="primary"尺寸为小size="small"点击按钮触发handleSearchList方法 -->
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<!-- el-button组件用于创建重置按钮设置按钮靠右浮动且右边距为15pxfloat:right; margin-right: 15px尺寸为小size="small"点击按钮触发handleResetSearch方法 -->
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<!-- 创建一个内联表单:inline="true"绑定数据模型为listQuery设置表单尺寸为小size="small"标签宽度为140px -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单中优惠券名称输入的表单项通过v-model双向绑定数据到listQuery.name设置输入框宽度样式类为input-width添加占位提示文本 -->
<el-form-item label="优惠券名称:">
<el-input v-model="listQuery.name" class="input-width" placeholder="优惠券名称"></el-input>
</el-form-item>
<!-- 表单中优惠券类型选择的表单项通过v-model双向绑定数据到listQuery.type设置占位提示文本为全部支持清空选择clearable输入框宽度样式类为input-width -->
<el-form-item label="优惠券类型:">
<el-select v-model="listQuery.type" placeholder="全部" clearable class="input-width">
<el-option v-for="item in typeOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-form>
</div>
</el-card>
<!-- 使用el-card组件创建一个操作相关的容器设置阴影效果为无 -->
<el-card class="operate-container" shadow="never">
<!-- 使用element-ui的图标元素el-icon-tickets表示相关图标 -->
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<!-- el-button组件用于创建添加按钮设置按钮尺寸为迷你size="mini"样式类为btn-add点击按钮触发handleAdd方法 -->
<el-button size="mini" class="btn-add" @click="handleAdd()"></el-button>
</el-card>
<!-- 创建一个用于展示优惠券数据表格的容器div -->
<div class="table-container">
<!-- 使用el-table组件展示优惠券数据列表绑定数据来源为list设置宽度为100%监听行选择改变事件@selection-change并绑定handleSelectionChange方法通过v-loading指令绑定加载状态为listLoading显示表格边框 -->
<el-table ref="couponTable"
:data="list"
style="width: 100%;"
@selection-change="handleSelectionChange"
v-loading="listLoading" border>
<!-- 定义一个选择列type="selection"用于多选操作宽度为60px内容居中对齐 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 定义表格列编号宽度为100px内容居中对齐通过插槽slot-scope展示对应行数据中的id属性 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 定义表格列优惠劵名称内容居中对齐通过插槽展示对应行数据中的name属性 -->
<el-table-column label="优惠劵名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 定义表格列优惠券类型宽度为100px内容居中对齐通过双花括号插值表达式绑定对应行数据中的type属性并使用formatType过滤器格式化显示内容 -->
<el-table-column label="优惠券类型" width="100" align="center">
<template slot-scope="scope">{{scope.row.type | formatType}}</template>
</el-table-column>
<!-- 定义表格列可使用商品宽度为100px内容居中对齐通过双花括号插值表达式绑定对应行数据中的useType属性并使用formatUseType过滤器格式化显示内容 -->
<el-table-column label="可使用商品" width="100" align="center">
<template slot-scope="scope">{{scope.row.useType | formatUseType}}</template>
</el-table-column>
<!-- 定义表格列使用门槛宽度为140px内容居中对齐通过插槽展示使用门槛相关文本格式为满XX元可用其中XX为对应行数据中的minPoint属性值 -->
<el-table-column label="使用门槛" width="140" align="center">
<template slot-scope="scope">{{scope.row.minPoint}}元可用</template>
</el-table-column>
<!-- 定义表格列面值宽度为100px内容居中对齐通过插槽展示对应行数据中的amount属性值并添加字作为后缀 -->
<el-table-column label="面值" width="100" align="center">
<template slot-scope="scope">{{scope.row.amount}}</template>
</el-table-column>
<!-- 定义表格列适用平台宽度为100px内容居中对齐通过双花括号插值表达式绑定对应行数据中的platform属性并使用formatPlatform过滤器格式化显示内容 -->
<el-table-column label="适用平台" width="100" align="center">
<template slot-scope="scope">{{scope.row.platform | formatPlatform}}</template>
</el-table-column>
<!-- 定义表格列有效期宽度为180px内容居中对齐通过双花括号插值表达式分别绑定对应行数据中的startTime和endTime属性并使用formatDate过滤器格式化显示日期内容中间用字连接 -->
<el-table-column label="有效期" width="180" align="center">
<template slot-scope="scope">{{scope.row.startTime|formatDate}}{{scope.row.endTime|formatDate}}</template>
</el-table-column>
<!-- 定义表格列状态宽度为100px内容居中对齐通过双花括号插值表达式绑定对应行数据中的endTime属性并使用formatStatus过滤器根据日期判断并格式化显示优惠券状态 -->
<el-table-column label="状态" width="100" align="center">
<template slot-scope="scope">{{scope.row.endTime | formatStatus}}</template>
</el-table-column>
<!-- 定义表格列操作宽度为180px内容居中对齐通过插槽在单元格内放置多个按钮分别点击按钮触发对应的操作方法handleViewhandleUpdatehandleDelete -->
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleView(scope.$index, scope.row)">查看</el-button>
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">
编辑</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 创建一个用于放置分页组件的容器div -->
<div class="pagination-container">
<!-- 使用el-pagination组件实现分页功能设置背景色background绑定页面尺寸改变size-change和当前页改变current-change的事件处理方法配置分页布局当前页每页数量可选页面尺寸列表以及总记录数等属性 -->
<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>
</div>
</template>
<script>
// @/api/couponfetchListdeleteCoupon
import {fetchList, deleteCoupon} from '@/api/coupon';
// @/utils/dateformatDate
import {formatDate} from '@/utils/date';
//
const defaultListQuery = {
pageNum: 1,
pageSize: 10,
name: null,
type: null
};
//
const defaultTypeOptions = [
{
label: '全场赠券',
value: 0
},
{
label: '会员赠券',
value: 1
},
{
label: '购物赠券',
value: 2
},
{
label: '注册赠券',
value: 3
}
];
export default
name: 'couponList',
data() {
return {
// defaultListQuery
listQuery: Object.assign({}, defaultListQuery),
// defaultTypeOptions
typeOptions: Object.assign({}, defaultTypeOptions),
//
list: null,
//
total: null,
//
listLoading: false,
// selection-change
multipleSelection: []
}
},
created() {
// getList
this.getList();
},
filters: {
// formatTypetypedefaultTypeOptions
formatType(type) {
for (let i = 0; i < defaultTypeOptions.length; i++) {
if (type === defaultTypeOptions[i].value) {
return defaultTypeOptions[i].label;
}
}
return '';
},
// formatUseType使useType使
formatUseType(useType) {
if (useType === 0) {
return '全场通用';
} else if (useType === 1) {
return '指定分类';
} else {
return '指定商品';
}
},
// formatPlatformplatform
formatPlatform(platform) {
if (platform === 1) {
return '移动平台';
} else if (platform === 2) {
return 'PC平台';
} else {
return '全平台';
}
},
// formatDatenullN/AformatDate'yyyy-MM-dd'
formatDate(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd')
},
// formatStatusendTime
formatStatus(endTime) {
let now = new Date().getTime();
let endDate = new Date(endTime);
if (endDate > now) {
return '未过期'
} else {
return '已过期';
}
}
},
methods:
// defaultListQuery
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
// 1getList
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
// multipleSelection便
handleSelectionChange(val) {
this.multipleSelection = val;
},
// 1valgetList
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// valgetList
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
// $router.push'/sms/addCoupon'
handleAdd() {
this.$router.push({path: '/sms/addCoupon'})
},
// $router.push'/sms/couponHistory'IDrow.id
handleView(index, row) {
this.$router.push({path: '/sms/couponHistory', query: {id: row.id}})
},
// $router.push'/sms/updateCoupon'IDrow.id
handleUpdate(index, row) {
this.$router.push({path: '/sms/updateCoupon', query: {id: row.id}})
},
// $confirmdeleteCouponIDrow.id$messagegetList
handleDelete(index, row) {
this.$confirm('是否进行删除操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteCoupon(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
})
},
//

@ -0,0 +1,29 @@
<template> 
<coupon-detail :isEdit="true"></coupon-detail>
</template>
<script>
import CouponDetail from './components/CouponDetail'
export default {
name: 'updateCoupon',
components: { CouponDetail }
}
</script>
<style scoped>
</style>
#abc
<template>
<!-- 使用CouponDetail组件来展示编辑优惠券的相关界面通过属性绑定将isEdit设置为true表示当前处于编辑优惠券的状态 -->
<coupon-detail :isEdit="true"></coupon-detail>
</template>
<script>
// ./components/CouponDetailCouponDetailCouponDetailVue
import CouponDetail from './components/CouponDetail'
export default {
name: 'updateCoupon',
// componentsCouponDetailtemplate使<coupon-detail>便
components: { CouponDetail }
}
</script>

@ -0,0 +1,655 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="活动名称:">
<el-input v-model="listQuery.keyword" class="input-width" placeholder="活动名称" clearable></el-input>
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加活动</el-button>
<el-button size="mini" class="btn-add" @click="handleShowSessionList()"></el-button>
</el-card>
<div class="table-container">
<el-table ref="flashTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column type="selection" width="60" align="center"></el-table-column>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="活动标题" align="center">
<template slot-scope="scope">{{scope.row.title}}</template>
</el-table-column>
<el-table-column label="活动状态" width="140" align="center">
<template slot-scope="scope">{{scope.row |formatActiveStatus}}</template>
</el-table-column>
<el-table-column label="开始时间" width="140" align="center">
<template slot-scope="scope">{{scope.row.startDate | formatDate}}</template>
</el-table-column>
<el-table-column label="结束时间" width="140" align="center">
<template slot-scope="scope">{{scope.row.endDate | formatDate}}</template>
</el-table-column>
<el-table-column label="上线/下线" width="200" align="center">
<template slot-scope="scope">
<el-switch
@change="handleStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.status">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleSelectSession(scope.$index, scope.row)">设置商品
</el-button>
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">
编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</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="dialogVisible"
width="40%">
<el-form :model="flashPromotion"
ref="flashPromotionForm"
label-width="150px" size="small">
<el-form-item label="活动标题:">
<el-input v-model="flashPromotion.title" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="开始时间:">
<el-date-picker
v-model="flashPromotion.startDate"
type="date"
placeholder="请选择时间">
</el-date-picker>
</el-form-item>
<el-form-item label="结束时间:">
<el-date-picker
v-model="flashPromotion.endDate"
type="date"
placeholder="请选择时间">
</el-date-picker>
</el-form-item>
<el-form-item label="上线/下线">
<el-radio-group v-model="flashPromotion.status">
<el-radio :label="1">上线</el-radio>
<el-radio :label="0">下线</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList, updateStatus, deleteFlash, createFlash, updateFlash} from '@/api/flash';
import {formatDate} from '@/utils/date';
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
keyword: null
};
const defaultFlashPromotion = {
id: null,
title: null,
startDate: null,
endDate: null,
status: 0
};
export default {
name: 'flashPromotionList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
list: null,
total: null,
listLoading: false,
dialogVisible: false,
flashPromotion: Object.assign({}, defaultFlashPromotion),
isEdit: false
}
},
created() {
this.getList();
},
filters: {
formatActiveStatus(row) {
let nowDate = new Date();
let startDate = new Date(row.startDate);
let endDate = new Date(row.endDate);
if (nowDate.getTime() >= startDate.getTime() && nowDate.getTime() <= endDate.getTime()) {
return '活动进行中';
} else if (nowDate.getTime() > endDate.getTime()) {
return '活动已结束';
} else {
return '活动未开始';
}
},
formatDate(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd')
}
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleAdd() {
this.dialogVisible = true;
this.isEdit = false;
this.flashPromotion = Object.assign({},defaultFlashPromotion);
},
handleShowSessionList() {
this.$router.push({path: '/sms/flashSession'})
},
handleStatusChange(index, row) {
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id, {status: row.status}).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '取消修改'
});
this.getList();
});
},
handleDelete(index, row) {
this.$confirm('是否要删除该活动?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteFlash(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
handleUpdate(index, row) {
this.dialogVisible = true;
this.isEdit = true;
this.flashPromotion = Object.assign({},row);
},
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateFlash(this.flashPromotion.id,this.flashPromotion).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
} else {
createFlash(this.flashPromotion).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
}
})
},
handleSelectSession(index,row){
this.$router.push({path:'/sms/selectSession',query:{flashPromotionId: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>
<style></style>
#abc
<template>
<!-- 创建一个类名为 `app-container` `div` 元素作为整个页面的容器用于布局和包裹内部的各个模块 -->
<div class="app-container">
<!-- 使用 `el-card` 组件创建一个筛选搜索的容器设置阴影效果为无 -->
<el-card class="filter-container" shadow="never">
<div>
<!-- 使用 `el-icon-search` 图标表示搜索图标 -->
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<!-- 创建一个查询搜索按钮设置按钮靠右浮动样式为主要按钮`type="primary"`尺寸为小`size="small"`点击按钮时触发 `handleSearchList` 方法 -->
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<!-- 创建一个重置按钮设置按钮靠右浮动且右边距为 `15px`尺寸为小`size="small"`点击按钮时触发 `handleResetSearch` 方法 -->
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<!-- 创建一个内联表单`:inline="true"`绑定数据模型为 `listQuery`设置表单尺寸为小`size="small"`标签宽度为 `140px` -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 表单中活动名称输入的表单项通过 `v-model` 双向绑定数据到 `listQuery.keyword`设置输入框宽度样式类为 `input-width`添加占位提示文本支持清空输入框内容`clearable` -->
<el-form-item label="活动名称:">
<el-input v-model="listQuery.keyword" class="input-width" placeholder="活动名称" clearable></el-input>
</el-form-item>
</el-form>
</div>
</el-card>
<!-- 使用 `el-card` 组件创建一个操作相关的容器设置阴影效果为无 -->
<el-card class="operate-container" shadow="never">
<!-- 使用 `el-icon-tickets` 图标表示相关图标 -->
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<!-- 创建一个添加活动的按钮设置按钮尺寸为迷你`size="mini"`样式类为 `btn-add`点击按钮时触发 `handleAdd` 方法设置左边距为 `20px` -->
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加活动</el-button>
<!-- 创建一个查看秒杀时间段列表的按钮设置按钮尺寸为迷你`size="mini"`样式类为 `btn-add`点击按钮时触发 `handleShowSessionList` 方法 -->
<el-button size="mini" class="btn-add" @click="handleShowSessionList()"></el-button>
</el-card>
<!-- 创建一个用于展示活动数据表格的容器 `div` -->
<div class="table-container">
<!-- 使用 `el-table` 组件展示活动数据列表绑定数据来源为 `list`设置宽度为 `100%`通过 `v-loading` 指令绑定加载状态为 `listLoading`显示表格边框 -->
<el-table ref="flashTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<!-- 定义一个选择列`type="selection"`用于多选操作宽度为 `60px`内容居中对齐 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 定义表格列编号宽度为 `100px`内容居中对齐通过插槽`slot-scope`展示对应行数据中的 `id` 属性 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 定义表格列活动标题内容居中对齐通过插槽展示对应行数据中的 `title` 属性 -->
<el-table-column label="活动标题" align="center">
<template slot-scope="scope">{{scope.row.title}}</template>
</el-table-column>
<!-- 定义表格列活动状态宽度为 `140px`内容居中对齐通过双花括号插值表达式绑定对应行数据并使用 `formatActiveStatus` 过滤器根据时间判断并格式化显示活动状态 -->
<el-table-column label="活动状态" width="140" align="center">
<template slot-scope="scope">{{scope.row |formatActiveStatus}}</template>
</el-table-column>
<!-- 定义表格列开始时间宽度为 `140px`内容居中对齐通过双花括号插值表达式绑定对应行数据中的 `startDate` 属性并使用 `formatDate` 过滤器格式化显示日期内容 -->
<el-table-column label="开始时间" width="140" align="center">
<template slot-scope="scope">{{scope.row.startDate | formatDate}}</template>
</el-table-column>
<!-- 定义表格列结束时间宽度为 `140px`内容居中对齐通过双花括号插值表达式绑定对应行数据中的 `endDate` 属性并使用 `formatDate` 过滤器格式化显示日期内容 -->
<el-table-column label="结束时间" width="140" align="center">
<template slot-scope="scope">{{scope.row.endDate | formatDate}}</template>
</el-table-column>
<!-- 定义表格列上线/下线宽度为 `200px`内容居中对齐在单元格内使用 `el-switch` 组件实现开关功能绑定 `change` 事件到 `handleStatusChange` 方法设置开关的激活值和未激活值双向绑定数据到对应行数据的 `status` 属性 -->
<el-table-column label="上线/下线" width="200" align="center">
<template slot-scope="scope">
<el-switch
@change="handleStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.status">
</el-switch>
</template>
</el-table-column>
<!-- 定义表格列操作宽度为 `180px`内容居中对齐通过插槽在单元格内放置多个按钮分别点击按钮触发对应的操作方法`handleSelectSession``handleUpdate``handleDelete` -->
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleSelectSession(scope.$index, scope.row)">设置商品
</el-button>
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">
编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 创建一个用于放置分页组件的容器 `div` -->
<div class="pagination-container">
<!-- 使用 `el-pagination` 组件实现分页功能设置背景色`background`绑定页面尺寸改变`size-change`和当前页改变`current-change`的事件处理方法配置分页布局当前页每页数量可选页面尺寸列表以及总记录数等属性 -->
<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` 组件创建一个对话框用于添加或编辑活动的相关操作绑定对话框的显示状态为 `dialogVisible`设置标题为添加活动宽度为页面宽度的 `40%` -->
<el-dialog
title="添加活动"
:visible.sync="dialogVisible"
width="40%">
<!-- 在对话框内创建一个表单绑定数据模型为 `flashPromotion`设置表单引用为 `flashPromotionForm`标签宽度为 `150px`尺寸为小`size="small"` -->
<el-form :model="flashPromotion"
ref="flashPromotionForm"
label-width="150px" size="small">
<!-- 表单中活动标题输入的表单项通过 `v-model` 双向绑定数据到 `flashPromotion.title`设置输入框宽度为 `250px` -->
<el-form-item label="活动标题:">
<el-input v-model="flashPromotion.title" style="width: 250px"></el-input>
</el-form-item>
<!-- 表单中开始时间选择的表单项使用 `el-date-picker` 组件选择日期双向绑定数据到 `flashPromotion.startDate`设置类型为 `date`添加占位提示文本 -->
<el-form-item label="开始时间:">
<el-date-picker
v-model="flashPromotion.startDate"
type="date"
placeholder="请选择时间">
</el-date-picker>
</el-form-item>
<!-- 表单中结束时间选择的表单项使用 `el-date-picker` 组件选择日期双向绑定数据到 `flashPromotion.endDate`设置类型为 `date`添加占位提示文本 -->
<el-form-item label="结束时间:">
<el-date-picker
v-model="flashPromotion.endDate"
type="date"
placeholder="请选择时间">
</el-date-picker>
</el-form-item>
<!-- 表单中上线/下线选择的表单项使用 `el-radio-group` 组件实现单选功能双向绑定数据到 `flashPromotion.status` -->
<el-form-item label="上线/下线">
<el-radio-group v-model="flashPromotion.status">
<el-radio :label="1">上线</el-radio>
<el-radio :label="0">下线</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<!-- 在对话框的底部插槽`slot="footer"`内添加两个按钮分别用于取消和确定操作点击取消按钮隐藏对话框点击确定按钮触发 `handleDialogConfirm` 方法 -->
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
// '@/api/flash'
import {fetchList, updateStatus, deleteFlash, createFlash, updateFlash} from '@/api/flash';
// '@/utils/date'formatDate便
import {formatDate} from '@/utils/date';
//
const defaultListQuery = {
pageNum: 1, // 1
pageSize: 5, // 5
keyword: null //
};
//
const defaultFlashPromotion = {
id: null, // null
title: null, //
startDate: null, //
endDate: null, //
status: 0 // 0线
};
export default {
name: 'flashPromotionList',
data() {
return {
// Object.assigndefaultListQuery
listQuery: Object.assign({}, defaultListQuery),
// null
list: null,
// null
total: null,
// falsetruefalse
listLoading: false,
// /falsetrue
dialogVisible: false,
// defaultFlashPromotion便
flashPromotion: Object.assign({}, defaultFlashPromotion),
// truefalsefalse
isEdit: false
}
},
created() {
// getList
this.getList();
},
filters: {
// formatActiveStatus
formatActiveStatus(row) {
let nowDate = new Date(); //
let startDate = new Date(row.startDate); // Date便
let endDate = new Date(row.endDate); // Date
//
if (nowDate.getTime() >= startDate.getTime() && nowDate.getTime() <= endDate.getTime()) {
return '活动进行中';
}
//
else if (nowDate.getTime() > endDate.getTime()) {
return '活动已结束';
}
//
else {
return '活动未开始';
}
},
// formatDatenull'N/A'formatDate'yyyy-MM-dd'
formatDate(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd')
}
},
methods: {
// defaultListQuery便
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
// 1getListkeyword
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
// 1valgetList
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// valgetList
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
// dialogVisibletrue使isEditfalseflashPromotiondefaultFlashPromotion
handleAdd() {
this.dialogVisible = true;
this.isEdit = false;
this.flashPromotion = Object.assign({},defaultFlashPromotion);
},
// Vue Routerpush'/sms/flashSession'
handleShowSessionList() {
this.$router.push({path: '/sms/flashSession'})
},
// 线/线updateStatusidgetList
handleStatusChange(index, row) {
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id, {status: row.status}).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '取消修改'
});
this.getList();
});
},
// deleteFlashidgetList
handleDelete(index, row) {
this.$confirm('是否要删除该活动?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteFlash(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
// dialogVisibletrue使isEdittrueflashPromotionObject.assignrow便
handleUpdate(index, row) {
this.dialogVisible = true;
this.isEdit = true;
this.flashPromotion = Object.assign({},row);
},
// /isEditupdateFlashidflashPromotiongetListcreateFlashflashPromotion
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateFlash(this.flashPromotion.id,this.flashPromotion).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
} else {
createFlash(this.flashPromotion).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
}
})
},
// Vue Routerpush'/sms/selectSession'idquery便使
handleSelectSession(index,row){
this.$router.push({path:'/sms/selectSession',query:{flashPromotionId:row.id}})
},
// listLoadingtruefetchListlistQuerylistLoadingfalselisttotal
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
}
}
}
</script>
<style></style>

@ -0,0 +1,682 @@
<template> 
<div class="app-container">
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button size="mini" class="btn-add" @click="handleSelectProduct()" style="margin-left: 20px">添加</el-button>
</el-card>
<div class="table-container">
<el-table ref="productRelationTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.product.name}}</template>
</el-table-column>
<el-table-column label="货号" width="140" align="center">
<template slot-scope="scope">NO.{{scope.row.product.productSn}}</template>
</el-table-column>
<el-table-column label="商品价格" width="100" align="center">
<template slot-scope="scope">{{scope.row.product.price}}</template>
</el-table-column>
<el-table-column label="剩余数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.product.stock}}</template>
</el-table-column>
<el-table-column label="秒杀价格" width="100" align="center">
<template slot-scope="scope">
<p v-if="scope.row.flashPromotionPrice!==null">
{{scope.row.flashPromotionPrice}}
</p>
</template>
</el-table-column>
<el-table-column label="秒杀数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.flashPromotionCount}}</template>
</el-table-column>
<el-table-column label="限购数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.flashPromotionLimit}}</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="操作" width="100" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</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="selectDialogVisible" width="50%">
<el-input v-model="dialogData.listQuery.keyword"
style="width: 250px;margin-bottom: 20px"
size="small"
placeholder="商品名称搜索">
<el-button slot="append" icon="el-icon-search" @click="handleSelectSearch()"></el-button>
</el-input>
<el-table :data="dialogData.list"
@selection-change="handleDialogSelectionChange" border>
<el-table-column type="selection" width="60" align="center"></el-table-column>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="货号" width="160" align="center">
<template slot-scope="scope">NO.{{scope.row.productSn}}</template>
</el-table-column>
<el-table-column label="价格" width="120" align="center">
<template slot-scope="scope">{{scope.row.price}}</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
background
@size-change="handleDialogSizeChange"
@current-change="handleDialogCurrentChange"
layout="prev, pager, next"
:current-page.sync="dialogData.listQuery.pageNum"
:page-size="dialogData.listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="dialogData.total">
</el-pagination>
</div>
<div style="clear: both;"></div>
<div slot="footer">
<el-button size="small" @click="selectDialogVisible = false"> </el-button>
<el-button size="small" type="primary" @click="handleSelectDialogConfirm()"> </el-button>
</div>
</el-dialog>
<el-dialog title="编辑秒杀商品信息"
:visible.sync="editDialogVisible"
width="40%">
<el-form :model="flashProductRelation"
ref="flashProductRelationForm"
label-width="150px" size="small">
<el-form-item label="商品名称:">
<span>{{flashProductRelation.product.name}}</span>
</el-form-item>
<el-form-item label="货号:">
<span>NO.{{flashProductRelation.product.productSn}}</span>
</el-form-item>
<el-form-item label="商品价格:">
<span>{{flashProductRelation.product.price}}</span>
</el-form-item>
<el-form-item label="秒杀价格:">
<el-input v-model="flashProductRelation.flashPromotionPrice" class="input-width">
<template slot="prepend"></template>
</el-input>
</el-form-item>
<el-form-item label="剩余数量:">
<span>{{flashProductRelation.product.stock}}</span>
</el-form-item>
<el-form-item label="秒杀数量:">
<el-input v-model="flashProductRelation.flashPromotionCount" class="input-width"></el-input>
</el-form-item>
<el-form-item label="限购数量:">
<el-input v-model="flashProductRelation.flashPromotionLimit" class="input-width"></el-input>
</el-form-item>
<el-form-item label="排序:">
<el-input v-model="flashProductRelation.sort" class="input-width"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editDialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleEditDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,createFlashProductRelation,deleteFlashProductRelation,updateFlashProductRelation} from '@/api/flashProductRelation';
import {fetchList as fetchProductList} from '@/api/product';
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
flashPromotionId: null,
flashPromotionSessionId: null
};
export default {
name:'flashPromotionProductRelationList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
list: null,
total: null,
listLoading: false,
dialogVisible: false,
selectDialogVisible:false,
dialogData:{
list: null,
total: null,
multipleSelection:[],
listQuery:{
keyword: null,
pageNum: 1,
pageSize: 5
}
},
editDialogVisible:false,
flashProductRelation:{
product:{}
}
}
},
created(){
this.listQuery.flashPromotionId=this.$route.query.flashPromotionId;
this.listQuery.flashPromotionSessionId=this.$route.query.flashPromotionSessionId;
this.getList();
},
methods:{
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleSelectProduct(){
this.selectDialogVisible=true;
this.getDialogList();
},
handleUpdate(index,row){
this.editDialogVisible = true;
this.flashProductRelation = Object.assign({},row);
},
handleDelete(index,row){
this.$confirm('是否要删除该商品?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteFlashProductRelation(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
handleSelectSearch(){
this.getDialogList();
},
handleDialogSizeChange(val) {
this.dialogData.listQuery.pageNum = 1;
this.dialogData.listQuery.pageSize = val;
this.getDialogList();
},
handleDialogCurrentChange(val) {
this.dialogData.listQuery.pageNum = val;
this.getDialogList();
},
handleDialogSelectionChange(val){
this.dialogData.multipleSelection = val;
},
handleSelectDialogConfirm(){
if (this.dialogData.multipleSelection < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
let selectProducts = [];
for (let i = 0; i < this.dialogData.multipleSelection.length; i++) {
selectProducts.push({
productId:this.dialogData.multipleSelection[i].id,
flashPromotionId:this.listQuery.flashPromotionId,
flashPromotionSessionId:this.listQuery.flashPromotionSessionId
});
}
this.$confirm('使用要进行添加操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
createFlashProductRelation(selectProducts).then(response=>{
this.selectDialogVisible=false;
this.dialogData.multipleSelection=[];
this.getList();
this.$message({
type: 'success',
message: '添加成功!'
});
});
});
},
handleEditDialogConfirm(){
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateFlashProductRelation(this.flashProductRelation.id,this.flashProductRelation).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.editDialogVisible =false;
this.getList();
})
})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
getDialogList(){
fetchProductList(this.dialogData.listQuery).then(response=>{
this.dialogData.list=response.data.list;
this.dialogData.total=response.data.total;
})
}
}
}
</script>
<style scoped>
.operate-container{
margin-top: 0;
}
.input-width{
width: 200px;
}
</style>
#abc
<template>
<!-- 创建一个类名为 `app-container` `div` 元素作为整个页面的容器用于布局和包裹内部的各个模块 -->
<div class="app-container">
<!-- 使用 `el-card` 组件创建一个操作相关的容器设置阴影效果为无 -->
<el-card class="operate-container" shadow="never">
<!-- 使用 `el-icon-tickets` 图标表示相关图标 -->
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<!-- 创建一个添加按钮设置按钮尺寸为迷你`size="mini"`样式类为 `btn-add`点击按钮时触发 `handleSelectProduct` 方法设置左边距为 `20px` -->
<el-button size="mini" class="btn-add" @click="handleSelectProduct()" style="margin-left: 20px">添加</el-button>
</el-card>
<!-- 创建一个用于展示商品相关数据表格的容器 `div` -->
<div class="table-container">
<!-- 使用 `el-table` 组件展示商品数据列表绑定数据来源为 `list`设置宽度为 `100%`通过 `v-loading` 指令绑定加载状态为 `listLoading`显示表格边框 -->
<el-table ref="productRelationTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<!-- 定义表格列编号宽度为 `100px`内容居中对齐通过插槽`slot-scope`展示对应行数据中的 `id` 属性 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 定义表格列商品名称内容居中对齐通过插槽展示对应行数据中 `product` 对象的 `name` 属性 -->
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.product.name}}</template>
</el-table-column>
<!-- 定义表格列货号宽度为 `140px`内容居中对齐通过插槽展示对应行数据中 `product` 对象的 `productSn` 属性并添加前缀 `NO.` -->
<el-table-column label="货号" width="140" align="center">
<template slot-scope="scope">NO.{{scope.row.product.productSn}}</template>
</el-table-column>
<!-- 定义表格列商品价格宽度为 `100px`内容居中对齐通过插槽展示对应行数据中 `product` 对象的 `price` 属性并添加前缀 `` -->
<el-table-column label="商品价格" width="100" align="center">
<template slot-scope="scope">{{scope.row.product.price}}</template>
</el-table-column>
<!-- 定义表格列剩余数量宽度为 `100px`内容居中对齐通过插槽展示对应行数据中 `product` 对象的 `stock` 属性 -->
<el-table-column label="剩余数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.product.stock}}</template>
</el-table-column>
<!-- 定义表格列秒杀价格宽度为 `100px`内容居中对齐通过 `v-if` 指令判断对应行数据中 `flashPromotionPrice` 属性是否不为 `null`如果是则展示该价格并添加前缀 `` -->
<el-table-column label="秒杀价格" width="100" align="center">
<template slot-scope="scope">
<p v-if="scope.row.flashPromotionPrice!==null">
{{scope.row.flashPromotionPrice}}
</p>
</template>
</el-table-column>
<!-- 定义表格列秒杀数量宽度为 `100px`内容居中对齐通过插槽展示对应行数据中的 `flashPromotionCount` 属性 -->
<el-table-column label="秒杀数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.flashPromotionCount}}</template>
</el-table-column>
<!-- 定义表格列限购数量宽度为 `100px`内容居中对齐通过插槽展示对应行数据中的 `flashPromotionLimit` 属性 -->
<el-table-column label="限购数量" width="100" align="center">
<template slot-scope="scope">{{scope.row.flashPromotionLimit}}</template>
</el-table-column>
<!-- 定义表格列排序宽度为 `100px`内容居中对齐通过插槽展示对应行数据中的 `sort` 属性 -->
<el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<!-- 定义表格列操作宽度为 `100px`内容居中对齐通过插槽在单元格内放置多个按钮分别点击按钮触发对应的操作方法`handleUpdate``handleDelete` -->
<el-table-column label="操作" width="100" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 创建一个用于放置分页组件的容器 `div` -->
<div class="pagination-container">
<!-- 使用 `el-pagination` 组件实现分页功能设置背景色`background`绑定页面尺寸改变`size-change`和当前页改变`current-change`的事件处理方法配置分页布局当前页每页数量可选页面尺寸列表以及总记录数等属性 -->
<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` 组件创建一个对话框用于选择商品的相关操作绑定对话框的显示状态为 `selectDialogVisible`设置标题为选择商品宽度为页面宽度的 `50%` -->
<el-dialog title="选择商品" :visible.sync="selectDialogVisible" width="50%">
<!-- 创建一个输入框双向绑定数据到 `dialogData.listQuery.keyword`设置宽度为 `250px`下外边距为 `20px`尺寸为小`size="small"`添加占位提示文本在输入框右侧添加一个搜索图标按钮点击按钮触发 `handleSelectSearch` 方法 -->
<el-input v-model="dialogData.listQuery.keyword"
style="width: 250px;margin-bottom: 20px"
size="small"
placeholder="商品名称搜索">
<el-button slot="append" icon="el-icon-search" @click="handleSelectSearch()"></el-button>
</el-input>
<!-- 使用 `el-table` 组件在对话框内展示可选择的商品列表绑定数据来源为 `dialogData.list`绑定选择改变事件到 `handleDialogSelectionChange` 方法显示表格边框 -->
<el-table :data="dialogData.list"
@selection-change="handleDialogSelectionChange" border>
<!-- 定义一个选择列`type="selection"`用于多选操作宽度为 `60px`内容居中对齐 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 定义表格列商品名称内容居中对齐通过插槽展示对应行数据中的 `name` 属性 -->
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 定义表格列货号宽度为 `160px`内容居中对齐通过插槽展示对应行数据中的 `productSn` 属性并添加前缀 `NO.` -->
<el-table-column label="货号" width="160" align="center">
<template slot-scope="scope">NO.{{scope.row.productSn}}</template>
</el-table-column>
<!-- 定义表格列价格宽度为 `120px`内容居中对齐通过插槽展示对应行数据中的 `price` 属性并添加前缀 `` -->
<el-table-column label="价格" width="120" align="center">
<template slot-scope="scope">{{scope.row.price}}</template>
</el-table-column>
</el-table>
<!-- 在对话框内创建一个用于放置分页组件的容器 `div`用于对选择商品的列表进行分页操作 -->
<div class="pagination-container">
<!-- 使用 `el-pagination` 组件实现分页功能设置背景色`background`绑定页面尺寸改变`size-change`和当前页改变`current-change`的事件处理方法配置分页布局当前页每页数量可选页面尺寸列表以及总记录数等属性这里的相关属性绑定到 `dialogData` 中的对应分页参数 -->
<el-pagination
background
@size-change="handleDialogSizeChange"
@current-change="handleDialogCurrentChange"
layout="prev, pager, next"
:current-page.sync="dialogData.listQuery.pageNum"
:page-size="dialogData.listQuery.pageSize"
:page-sizes="[5, 10, 15]"
:total="dialogData.total">
</el-pagination>
</div>
<!-- 清除浮动确保后续元素布局不受影响 -->
<div style="clear: both;"></div>
<!-- 在对话框的底部插槽`slot="footer"`内添加两个按钮分别用于取消和确定操作点击取消按钮隐藏对话框点击确定按钮触发 `handleSelectDialogConfirm` 方法 -->
<div slot="footer">
<el-button size="small" @click="selectDialogVisible = false"> </el-button>
<el-button size="small" type="primary" @click="handleSelectDialogConfirm()"> </el-button>
</div>
</el-dialog>
<!-- 使用 `el-dialog` 组件创建一个对话框用于编辑秒杀商品信息的相关操作绑定对话框的显示状态为 `editDialogVisible`设置标题为编辑秒杀商品信息宽度为页面宽度的 `40%` -->
<el-dialog title="编辑秒杀商品信息"
:visible.sync="editDialogVisible"
width="40%">
<!-- 在对话框内创建一个表单绑定数据模型为 `flashProductRelation`设置表单引用为 `flashProductRelationForm`标签宽度为 `150px`尺寸为小`size="small"` -->
<el-form :model="flashProductRelation"
ref="flashProductRelationForm"
label-width="150px" size="small">
<!-- 表单中商品名称显示的表单项通过 `span` 标签展示对应 `flashProductRelation` `product` 对象的 `name` 属性此处为只读展示不进行输入操作 -->
<el-form-item label="商品名称:">
<span>{{flashProductRelation.product.name}}</span>
</el-form-item>
<!-- 表单中货号显示的表单项通过 `span` 标签展示对应 `flashProductRelation` `product` 对象的 `productSn` 属性添加前缀 `NO.`同样为只读展示 -->
<el-form-item label="货号:">
<span>NO.{{flashProductRelation.product.productSn}}</span>
</el-form-item>
<!-- 表单中商品价格显示的表单项通过 `span` 标签展示对应 `flashProductRelation` `product` 对象的 `price` 属性添加前缀 ``也是只读展示 -->
<el-form-item label="商品价格:">
<span>{{flashProductRelation.product.price}}</span>
</el-form-item>
<!-- 表单中秒杀价格输入的表单项通过 `el-input` 组件双向绑定数据到 `flashProductRelation.flashPromotionPrice`设置输入框宽度样式类为 `input-width`并在输入框前添加前缀 `` -->
<el-form-item label="秒杀价格:">
<el-input v-model="flashProductRelation.flashPromotionPrice" class="input-width">
<template slot="prepend"></template>
</el-input>
</el-form-item>
<!-- 表单中剩余数量显示的表单项通过 `span` 标签展示对应 `flashProductRelation` `product` 对象的 `stock` 属性为只读展示 -->
<el-form-item label="剩余数量:">
<span>{{flashProductRelation.product.stock}}</span>
</el-form-item>
<!-- 表单中秒杀数量输入的表单项通过 `el-input` 组件双向绑定数据到 `flashProductRelation.flashPromotionCount`设置输入框宽度样式类为 `input-width` -->
<el-form-item label="秒杀数量:">
<el-input v-model="flashProductRelation.flashPromotionCount" class="input-width"></el-input>
</el-form-item>
<!-- 表单中限购数量输入的表单项通过 `el-input` 组件双向绑定数据到 `flashProductRelation.flashPromotionLimit`设置输入框宽度样式类为 `input-width` -->
<el-form-item label="限购数量:">
<el-input v-model="flashProductRelation.flashPromotionLimit" class="input-width"></el-input>
</el-form-item>
<!-- 表单中排序输入的表单项通过 `el-input` 组件双向绑定数据到 `flashProductRelation.sort`设置输入框宽度样式类为 `input-width` -->
<el-form-item label="排序:">
<el-input v-model="flashProductRelation.sort" class="input-width"></el-input>
</el-form-item>
</el-form>
<!-- 在对话框的底部插槽`slot="footer"`内添加两个按钮分别用于取消和确定操作点击取消按钮隐藏对话框点击确定按钮触发 `handleEditDialogConfirm` 方法 -->
<span slot="footer" class="dialog-footer">
<el-button @click="editDialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleEditDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
// '@/api/flashProductRelation'fetchListcreateFlashProductRelationdeleteFlashProductRelationupdateFlashProductRelationAPI
import {fetchList, createFlashProductRelation, deleteFlashProductRelation, updateFlashProductRelation} from '@/api/flashProductRelation';
// '@/api/product'fetchProductList
import {fetchList as fetchProductList} from '@/api/product';
// ID
const defaultListQuery = {
pageNum: 1, //
pageSize: 5, // 5
flashPromotionId: null, // IDnull
flashPromotionSessionId: null // IDnull
};
export default {
name: 'flashPromotionProductRelationList',
data() {
return {
// Object.assigndefaultListQuery
listQuery: Object.assign({}, defaultListQuery),
// nullgetList
list: null,
// null
total: null,
// falsetruefalse
listLoading: false,
// falsetrue
dialogVisible: false,
// falsehandleSelectProducttrue
selectDialogVisible: false,
// nullnull
dialogData: {
list: null,
total: null,
multipleSelection: [],
listQuery: {
keyword: null,
pageNum: 1,
pageSize: 5
}
},
// falsehandleUpdatetrue
editDialogVisible: false,
// product便
flashProductRelation: {
product: {}
}
}
},
created() {
// this.$route.queryflashPromotionIdflashPromotionSessionIdlistQuery
this.listQuery.flashPromotionId = this.$route.query.flashPromotionId;
this.listQuery.flashPromotionSessionId = this.$route.query.flashPromotionSessionId;
// getListlistQuery使
this.getList();
},
methods: {
// 1valgetList
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// valgetList
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
// selectDialogVisibletrue使getDialogList便
handleSelectProduct() {
this.selectDialogVisible = true;
this.getDialogList();
},
// indexroweditDialogVisibletrue使Object.assignrowflashProductRelation便
handleUpdate(index, row) {
this.editDialogVisible = true;
this.flashProductRelation = Object.assign({}, row);
},
// indexrowdeleteFlashProductRelationIDrow.idgetList
handleDelete(index, row) {
this.$confirm('是否要删除该商品?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteFlashProductRelation(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
// getDialogListdialogData.listQuery
handleSelectSearch() {
this.getDialogList();
},
// handleSizeChange1valgetDialogList
handleDialogSizeChange(val) {
this.dialogData.listQuery.pageNum = 1;
this.dialogData.listQuery.pageSize = val;
this.getDialogList();
},
// handleCurrentChangevalgetDialogList
handleDialogCurrentChange(val) {
this.dialogData.listQuery.pageNum = val;
this.getDialogList();
},
// valdialogDatamultipleSelection便
handleDialogSelectionChange(val) {
this.dialogData.multipleSelection = val;
},
// dialogData.multipleSelection1IDIDthis.listQuery.flashPromotionIdIDthis.listQuery.flashPromotionSessionIdselectProductscreateFlashProductRelationselectProductsgetList
handleSelectDialogConfirm() {
if (this.dialogData.multipleSelection.length < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
let selectProducts = [];
for (let i = 0; i < this.dialogData.multipleSelection.length; i++) {
selectProducts.push({
productId: this.dialogData.multipleSelection[i].id,
flashPromotionId: this.listQuery.flashPromotionId,
flashPromotionSessionId: this.listQuery.flashPromotionSessionId
});
}
this.$confirm('使用要进行添加操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
createFlashProductRelation(selectProducts).then(response => {
this.selectDialogVisible = false;
this.dialogData.multipleSelection = [];
this.getList();
this.$message({
type: 'success',
message: '添加成功!'
});
});
});
},
// updateFlashProductRelationIDthis.flashProductRelation.idflashProductRelationgetList使
handleEditDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateFlashProductRelation(this.flashProductRelation.id, this.flashProductRelation).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.editDialogVisible = false;
this.getList();
});
});
},
// listLoadingtruefetchListlistQuerylistLoadingfalselisttotal
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
// fetchProductListdialogData.listQuerydialogDatalistdialogDatatotal便
getDialogList() {
fetchProductList(this.dialogData.listQuery).then(response => {
this.dialogData.list = response.data.list;
this.dialogData.total = response.data.total;
});
}
}
}
</script>

@ -0,0 +1,181 @@
<template> 
<div class="app-container">
<el-card shadow="never" class="operate-container">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
</el-card>
<div class="table-container">
<el-table ref="selectSessionTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="秒杀时间段名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="每日开始时间" align="center">
<template slot-scope="scope">{{scope.row.startTime | formatTime}}</template>
</el-table-column>
<el-table-column label="每日结束时间" align="center">
<template slot-scope="scope">{{scope.row.endTime | formatTime}}</template>
</el-table-column>
<el-table-column label="商品数量" align="center">
<template slot-scope="scope">{{scope.row.productCount}}</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleShowRelation(scope.$index, scope.row)">商品列表
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import {fetchSelectList} from '@/api/flashSession';
import {formatDate} from '@/utils/date';
export default {
name: 'selectSessionList',
data() {
return {
list: null,
listLoading: false
}
},
created() {
this.getList();
},
filters:{
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'hh:mm:ss')
}
},
methods: {
handleShowRelation(index,row){
this.$router.push({path:'/sms/flashProductRelation',query:{
flashPromotionId:this.$route.query.flashPromotionId, flashPromotionSessionId:row.id}})
},
getList() {
this.listLoading = true;
fetchSelectList({flashPromotionId:this.$route.query.flashPromotionId}).then(response => {
this.listLoading = false;
this.list = response.data;
});
}
}
}
</script>
<style scoped>
.operate-container {
margin-top: 0;
}
</style>
#abc
<template>
<!-- 创建一个类名为 `app-container` `div` 元素作为整个页面的容器用于包裹内部的各个模块实现页面的整体布局 -->
<div class="app-container">
<!-- 使用 `el-card` 组件创建一个操作相关的容器设置阴影效果为无样式类为 `operate-container`可能用于展示页面的标题等操作相关元素 -->
<el-card shadow="never" class="operate-container">
<!-- 使用 `el-icon-tickets` 图标来展示一个特定的图标具体图标样式由对应图标库定义可能用于示意与当前数据列表相关的某种含义比如表示票务列表相关的操作等 -->
<i class="el-icon-tickets"></i>
<span>数据列表</span>
</el-card>
<!-- 创建一个用于展示表格数据的容器 `div`类名为 `table-container`用于将表格与其他元素在布局上进行区分 -->
<div class="table-container">
<!-- 使用 `el-table` 组件来展示数据列表通过 `ref` 属性为表格设置一个引用名称 `selectSessionTable`方便在 JavaScript 代码中通过 `$refs` 来获取该表格实例绑定数据来源为 `list`设置宽度为 `100%`通过 `v-loading` 指令绑定加载状态为 `listLoading`用于在数据加载时显示加载提示例如加载动画等并显示表格边框 -->
<el-table ref="selectSessionTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<!-- 定义表格的列标签为编号宽度设置为 `100px`内容在单元格内居中对齐通过插槽`slot-scope`获取对应行数据中的 `id` 属性值并展示在单元格内 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 定义表格的列标签为秒杀时间段名称内容在单元格内居中对齐通过插槽获取对应行数据中的 `name` 属性值并展示在单元格内用于展示秒杀时间段的名称信息 -->
<el-table-column label="秒杀时间段名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 定义表格的列标签为每日开始时间内容在单元格内居中对齐通过插槽获取对应行数据中的 `startTime` 属性值并使用名为 `formatTime` 的过滤器 Vue `filters` 选项中定义对时间数据进行格式化处理后展示在单元格内将时间转换为更易读的格式 -->
<el-table-column label="每日开始时间" align="center">
<template slot-scope="scope">{{scope.row.startTime | formatTime}}</template>
</el-table-column>
<!-- 定义表格的列标签为每日结束时间内容在单元格内居中对齐通过插槽获取对应行数据中的 `endTime` 属性值并同样使用 `formatTime` 过滤器对时间数据进行格式化处理后展示在单元格内 -->
<el-table-column label="每日结束时间" align="center">
<template slot-scope="scope">{{scope.row.endTime | formatTime}}</template>
</el-table-column>
<!-- 定义表格的列标签为商品数量内容在单元格内居中对齐通过插槽获取对应行数据中的 `productCount` 属性值并展示在单元格内用于展示与秒杀时间段相关的商品数量信息 -->
<el-table-column label="商品数量" align="center">
<template slot-scope="scope">{{scope.row.productCount}}</template>
</el-table-column>
<!-- 定义表格的列标签为操作内容在单元格内居中对齐通过插槽在单元格内放置一个按钮按钮尺寸为迷你`size="mini"`类型为文本按钮`type="text"`点击按钮时触发 `handleShowRelation` 方法并传入当前行的索引`scope.$index`和对应行的数据`scope.row`此处按钮文本为商品列表可能用于跳转到展示与该秒杀时间段相关商品列表的页面 -->
<el-table-column label="操作" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleShowRelation(scope.$index, scope.row)">商品列表
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
// `@/api/flashSession` `fetchSelectList` API
import {fetchSelectList} from '@/api/flashSession';
// `@/utils/date` `formatDate` 使便
import {formatDate} from '@/utils/date';
export default {
name: 'selectSessionList',
data() {
return {
// `null` `getList` 便
list: null,
// `false` `true` `false`
listLoading: false
}
},
created() {
// `getList` 使
this.getList();
},
filters: {
// `formatTime` `null``''` `N/A` `Date` `formatDate` `hh:mm:ss` ::使
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'hh:mm:ss')
}
},
methods: {
// `index``row` `this.$router.push` `/sms/flashProductRelation` `query` `flashPromotionId` ID `ID``row.id`
handleShowRelation(index, row) {
this.$router.push({path: '/sms/flashProductRelation', query: {
flashPromotionId: this.$route.query.flashPromotionId, flashPromotionSessionId: row.id
}});
},
// `listLoading` `true` `fetchSelectList` `flashPromotionId` `flashPromotionId` `listLoading` `false` `list`
getList() {
this.listLoading = true;
fetchSelectList({flashPromotionId: this.$route.query.flashPromotionId}).then(response => {
this.listLoading = false;
this.list = response.data;
});
}
}
}
</script>

@ -0,0 +1,476 @@
<template> 
<div class="app-container">
<el-card shadow="never" class="operate-container">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button size="mini" class="btn-add" @click="handleAdd()"></el-button>
</el-card>
<div class="table-container">
<el-table ref="flashSessionTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="秒杀时间段名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="每日开始时间" align="center">
<template slot-scope="scope">{{scope.row.startTime | formatTime}}</template>
</el-table-column>
<el-table-column label="每日结束时间" align="center">
<template slot-scope="scope">{{scope.row.endTime | formatTime}}</template>
</el-table-column>
<el-table-column label="启用" align="center">
<template slot-scope="scope">
<el-switch
@change="handleStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.status">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog
title="添加时间段"
:visible.sync="dialogVisible"
width="40%">
<el-form :model="flashSession"
ref="flashSessionForm"
label-width="150px" size="small">
<el-form-item label="秒杀时间段名称:">
<el-input v-model="flashSession.name" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="每日开始时间:">
<el-time-picker
v-model="flashSession.startTime"
placeholder="请选择时间">
</el-time-picker>
</el-form-item>
<el-form-item label="每日结束时间:">
<el-time-picker
v-model="flashSession.endTime"
placeholder="请选择时间">
</el-time-picker>
</el-form-item>
<el-form-item label="是否启用">
<el-radio-group v-model="flashSession.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">不启用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,updateStatus,deleteSession,createSession,updateSession} from '@/api/flashSession';
import {formatDate} from '@/utils/date';
const defaultFlashSession={
name:null,
startTime:null,
endTime:null,
status:0
};
export default {
name: 'flashPromotionSessionList',
data() {
return {
list: null,
listLoading: false,
dialogVisible:false,
isEdit:false,
flashSession:Object.assign({},defaultFlashSession)
}
},
created() {
this.getList();
},
filters:{
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'hh:mm:ss')
}
},
methods: {
handleAdd() {
this.dialogVisible = true;
this.isEdit = false;
this.flashSession = Object.assign({},defaultFlashSession);
},
handleStatusChange(index,row){
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id, {status: row.status}).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '取消修改'
});
this.getList();
});
},
handleUpdate(index,row){
this.dialogVisible = true;
this.isEdit = true;
this.flashSession = Object.assign({},row);
this.flashSession.startTime=new Date(row.startTime);
this.flashSession.endTime=new Date(row.endTime);
},
handleDelete(index,row){
this.$confirm('是否要删除该时间段?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteSession(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateSession(this.flashSession.id,this.flashSession).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
} else {
createSession(this.flashSession).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
}
})
},
getList() {
this.listLoading = true;
fetchList({}).then(response => {
this.listLoading = false;
this.list = response.data;
});
}
}
}
</script>
<style scoped>
.operate-container {
margin-top: 0;
}
</style>
#abc
<template>
<!-- 创建一个类名为 `app-container` `div` 元素作为整个页面内容的外层容器用于组织和布局页面内的各个组件和元素 -->
<div class="app-container">
<!-- 使用 `el-card` 组件创建一个操作相关的容器设置阴影效果为 `never`即无阴影并添加 `operate-container` 样式类该容器可能用于展示页面的一些操作按钮及标题等内容 -->
<el-card shadow="never" class="operate-container">
<!-- 使用 `el-icon-tickets` 图标元素来展示一个特定的图标具体图标样式由所使用的图标库决定在这里可能是用于示意与数据列表相关的某种含义比如表示和票务活动相关的业务场景 -->
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<!-- 创建一个 `el-button` 按钮元素设置按钮尺寸为 `mini`迷你型样式类为 `btn-add`点击按钮时会触发 `handleAdd` 方法按钮文本为添加用于触发添加新的秒杀时间段的操作 -->
<el-button size="mini" class="btn-add" @click="handleAdd()"></el-button>
</el-card>
<!-- 创建一个类名为 `table-container` `div` 元素用于包裹下面的 `el-table` 组件起到对表格进行布局划分的作用使其在页面上与其他元素区分开来 -->
<div class="table-container">
<!-- 使用 `el-table` 组件来展示秒杀时间段相关的数据列表通过 `ref` 属性为表格设置一个引用名称 `flashSessionTable`方便在 JavaScript 代码中通过 `$refs` 来获取该表格实例绑定数据来源为 `list`该数据会在 JavaScript 代码中获取并赋值设置表格宽度为 `100%`通过 `v-loading` 指令绑定加载状态为 `listLoading`用于在数据加载过程中显示加载提示例如加载动画等同时显示表格边框 -->
<el-table ref="flashSessionTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<!-- 定义表格的一列设置列标签为编号宽度为 `100px`内容在单元格内居中对齐通过 `slot-scope` 插槽获取对应行数据中的 `id` 属性值并展示在该单元格内用于展示每个秒杀时间段的唯一编号 -->
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 定义表格的一列设置列标签为秒杀时间段名称内容在单元格内居中对齐通过 `slot-scope` 插槽获取对应行数据中的 `name` 属性值并展示在该单元格内用于展示每个秒杀时间段的自定义名称 -->
<el-table-column label="秒杀时间段名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 定义表格的一列设置列标签为每日开始时间内容在单元格内居中对齐通过 `slot-scope` 插槽获取对应行数据中的 `startTime` 属性值并使用名为 `formatTime` 的过滤器 Vue `filters` 选项中定义对时间数据进行格式化处理后展示在该单元格内将时间转换为更易读的格式 `hh:mm:ss` -->
<el-table-column label="每日开始时间" align="center">
<template slot-scope="scope">{{scope.row.startTime | formatTime}}</template>
</el-table-column>
<!-- 定义表格的一列设置列标签为每日结束时间内容在单元格内居中对齐通过 `slot-scope` 插槽获取对应行数据中的 `endTime` 属性值并同样使用 `formatTime` 过滤器对时间数据进行格式化处理后展示在该单元格内 -->
<el-table-column label="每日结束时间" align="center">
<template slot-scope="scope">{{scope.row.endTime | formatTime}}</template>
</el-table-column>
<!-- 定义表格的一列设置列标签为启用内容在单元格内居中对齐在该单元格内放置一个 `el-switch` 开关组件用于切换秒杀时间段的启用状态开关组件绑定 `change` 事件到 `handleStatusChange` 方法传入当前行的索引`scope.$index`和对应行的数据`scope.row`设置 `active-value`开启时的值 `1``inactive-value`关闭时的值 `0`并通过 `v-model` 双向绑定到对应行数据中的 `status` 属性实现状态的可视化切换与数据绑定 -->
<el-table-column label="启用" align="center">
<template slot-scope="scope">
<el-switch
@change="handleStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.status">
</el-switch>
</template>
</el-table-column>
<!-- 定义表格的一列设置列标签为操作宽度为 `180px`内容在单元格内居中对齐通过 `slot-scope` 插槽在单元格内放置两个按钮分别用于编辑和删除对应的秒杀时间段编辑按钮点击时触发 `handleUpdate` 方法传入当前行的索引和对应行的数据删除按钮点击时触发 `handleDelete` 方法同样传入相应参数用于执行对应的数据操作 -->
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 使用 `el-dialog` 组件创建一个对话框用于添加或编辑秒杀时间段的相关操作设置对话框标题为添加时间段绑定对话框的显示状态为 `dialogVisible`通过 `:visible.sync` 实现双向绑定 JavaScript 代码中改变该变量值可控制对话框的显示与隐藏设置对话框宽度为页面宽度的 `40%` -->
<el-dialog
title="添加时间段"
:visible.sync="dialogVisible"
width="40%">
<!-- 在对话框内创建一个 `el-form` 表单组件用于收集用户输入的秒杀时间段相关信息绑定数据模型为 `flashSession`该数据对象在 JavaScript 代码中定义和操作设置表单引用为 `flashSessionForm`标签宽度为 `150px`表单尺寸为 `small`小型 -->
<el-form :model="flashSession"
ref="flashSessionForm"
label-width="150px" size="small">
<!-- 创建一个表单项目`el-form-item`设置标签为秒杀时间段名称在该项目内放置一个 `el-input` 输入框组件通过 `v-model` 双向绑定输入框的值到 `flashSession` 对象的 `name` 属性设置输入框宽度为 `250px`用于用户输入秒杀时间段的名称 -->
<el-form-item label="秒杀时间段名称:">
<el-input v-model="flashSession.name" style="width: 250px"></el-input>
</el-form-item>
<!-- 创建一个表单项目设置标签为每日开始时间在该项目内放置一个 `el-time-picker` 时间选择器组件通过 `v-model` 双向绑定时间选择器选择的值到 `flashSession` 对象的 `startTime` 属性设置占位提示文本为请选择时间用于用户选择秒杀时间段的每日开始时间 -->
<el-form-item label="每日开始时间:">
<el-time-picker
v-model="flashSession.startTime"
placeholder="请选择时间">
</el-time-picker>
</el-form-item>
<!-- 创建一个表单项目设置标签为每日结束时间在该项目内放置一个 `el-time-picker` 时间选择器组件通过 `v-model` 双向绑定时间选择器选择的值到 `flashSession` 对象的 `endTime` 属性设置占位提示文本为请选择时间用于用户选择秒杀时间段的每日结束时间 -->
<el-form-item label="每日结束时间:">
<el-time-picker
v-model="flashSession.endTime"
placeholder="请选择时间">
</el-time-picker>
</el-form-item>
<!-- 创建一个表单项目设置标签为是否启用在该项目内放置一个 `el-radio-group` 单选框组组件通过 `v-model` 双向绑定单选框组的值到 `flashSession` 对象的 `status` 属性用于用户选择该秒杀时间段是否启用单选框组内包含两个 `el-radio` 单选框分别对应启用`label` 值为 `1`和不启用`label` 值为 `0`两种状态 -->
<el-form-item label="是否启用">
<el-radio-group v-model="flashSession.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">不启用</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<!-- 在对话框的底部插槽`slot="footer"`内添加两个按钮分别用于取消和确定操作取消按钮点击时将 `dialogVisible` 设置为 `false`隐藏对话框确定按钮点击时触发 `handleDialogConfirm` 方法用于执行添加或编辑操作并根据操作结果处理后续逻辑 -->
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
// '@/api/flashSession' API
// fetchList
// updateStatus
// deleteSession ID
// createSession
// updateSession ID
import {fetchList, updateStatus, deleteSession, createSession, updateSession} from '@/api/flashSession';
// '@/utils/date'formatDate使便
import {formatDate} from '@/utils/date';
// defaultFlashSession
// namenull
// startTimenull
// endTimenull
// status00
const defaultFlashSession = {
name: null,
startTime: null,
endTime: null,
status: 0
};
export default {
name: 'flashPromotionSessionList',
data() {
return {
// listnullgetList
list: null,
// listLoadingfalsetruefalse
listLoading: false,
// dialogVisible/falsetrue
dialogVisible: false,
// isEdittruefalsefalse便
isEdit: false,
// flashSessionObject.assigndefaultFlashSession API
flashSession: Object.assign({}, defaultFlashSession)
}
},
created() {
// getList
this.getList();
},
filters: {
// formatTime使
// timenull'N/A'
// timeJavaScriptDateformatDate'hh:mm:ss'::
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'hh:mm:ss')
}
},
methods: {
// handleAdd
// dialogVisibletrue使/
// isEditfalse
// Object.assignflashSessiondefaultFlashSession
handleAdd() {
this.dialogVisible = true;
this.isEdit = false;
this.flashSession = Object.assign({}, defaultFlashSession);
},
// handleStatusChangeindexrow
// ?'warning'
// updateStatusrow.id{status: row.status}!
// catchgetList
handleStatusChange(index, row) {
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id, {status: row.status}).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '取消修改'
});
this.getList();
});
},
// handleUpdateindexrow
// dialogVisibletrue使/便
// isEdittrue API
// Object.assignrowflashSession便
// flashSessionstartTimeendTimeDate便
handleUpdate(index, row) {
this.dialogVisible = true;
this.isEdit = true;
this.flashSession = Object.assign({}, row);
this.flashSession.startTime = new Date(row.startTime);
this.flashSession.endTime = new Date(row.endTime);
},
// handleDeleteindexrow
// ?'warning'
// deleteSessionrow.id!getList
handleDelete(index, row) {
this.$confirm('是否要删除该时间段?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteSession(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
// handleDialogConfirm/
// ?'warning'
// isEdit
// isEdittrueupdateSessionflashSession.idflashSessiondialogVisiblefalsegetList使
// isEditfalsecreateSessionflashSessiondialogVisiblefalsegetList
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateSession(this.flashSession.id, this.flashSession).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible = false;
this.getList();
})
} else {
createSession(this.flashSession).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible = false;
this.getList();
})
}
})
},
// getList
// listLoadingtrue
// fetchListlistLoadingfalseresponse.datalist使
getList() {
this.listLoading = true;
fetchList({}).then(response => {
this.listLoading = false;
this.list = response.data;
});
}
}
}
</script>

@ -0,0 +1,839 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="商品名称:">
<el-input v-model="listQuery.productName" class="input-width" placeholder="商品名称"></el-input>
</el-form-item>
<el-form-item label="推荐状态:">
<el-select v-model="listQuery.recommendStatus" placeholder="全部" clearable class="input-width">
<el-option v-for="item in recommendOptions"
: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-button size="mini" class="btn-add" @click="handleSelectProduct()"></el-button>
</el-card>
<div class="table-container">
<el-table ref="newProductTable"
: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="120" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.productName}}</template>
</el-table-column>
<el-table-column label="是否推荐" width="200" align="center">
<template slot-scope="scope">
<el-switch
@change="handleRecommendStatusStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.recommendStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column label="排序" width="160" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<el-table-column label="状态" width="160" align="center">
<template slot-scope="scope">{{scope.row.recommendStatus | formatRecommendStatus}}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleEditSort(scope.$index, scope.row)">设置排序
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
: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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<el-dialog title="选择商品" :visible.sync="selectDialogVisible" width="50%">
<el-input v-model="dialogData.listQuery.keyword"
style="width: 250px;margin-bottom: 20px"
size="small"
placeholder="商品名称搜索">
<el-button slot="append" icon="el-icon-search" @click="handleSelectSearch()"></el-button>
</el-input>
<el-table :data="dialogData.list"
@selection-change="handleDialogSelectionChange" border>
<el-table-column type="selection" width="60" align="center"></el-table-column>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="货号" width="160" align="center">
<template slot-scope="scope">NO.{{scope.row.productSn}}</template>
</el-table-column>
<el-table-column label="价格" width="120" align="center">
<template slot-scope="scope">{{scope.row.price}}</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
background
@size-change="handleDialogSizeChange"
@current-change="handleDialogCurrentChange"
layout="prev, pager, next"
:current-page.sync="dialogData.listQuery.pageNum"
:page-size="dialogData.listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="dialogData.total">
</el-pagination>
</div>
<div style="clear: both;"></div>
<div slot="footer">
<el-button size="small" @click="selectDialogVisible = false"> </el-button>
<el-button size="small" type="primary" @click="handleSelectDialogConfirm()"> </el-button>
</div>
</el-dialog>
<el-dialog title="设置排序"
:visible.sync="sortDialogVisible"
width="40%">
<el-form :model="sortDialogData"
label-width="150px">
<el-form-item label="排序:">
<el-input v-model="sortDialogData.sort" style="width: 200px"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="sortDialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleUpdateSort" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,updateRecommendStatus,deleteHotProduct,createHotProduct,updateHotProductSort} from '@/api/hotProduct';
import {fetchList as fetchProductList} from '@/api/product';
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
productName: null,
recommendStatus: null
};
const defaultRecommendOptions = [
{
label: '未推荐',
value: 0
},
{
label: '推荐中',
value: 1
}
];
export default {
name: 'hotProductList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
recommendOptions: Object.assign({}, defaultRecommendOptions),
list: null,
total: null,
listLoading: false,
multipleSelection: [],
operates: [
{
label: "设为推荐",
value: 0
},
{
label: "取消推荐",
value: 1
},
{
label: "删除",
value: 2
}
],
operateType: null,
selectDialogVisible:false,
dialogData:{
list: null,
total: null,
multipleSelection:[],
listQuery:{
keyword: null,
pageNum: 1,
pageSize: 5
}
},
sortDialogVisible:false,
sortDialogData:{sort:0,id:null}
}
},
created() {
this.getList();
},
filters:{
formatRecommendStatus(status){
if(status===1){
return '推荐中';
}else{
return '未推荐';
}
}
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSelectionChange(val){
this.multipleSelection = val;
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleRecommendStatusStatusChange(index,row){
this.updateRecommendStatusStatus(row.id,row.recommendStatus);
},
handleDelete(index,row){
this.deleteProduct(row.id);
},
handleBatchOperate(){
if (this.multipleSelection < 1) {
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);
}
if (this.operateType === 0) {
//
this.updateRecommendStatusStatus(ids,1);
} else if (this.operateType === 1) {
//
this.updateRecommendStatusStatus(ids,0);
} else if(this.operateType===2){
//
this.deleteProduct(ids);
}else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
}
},
handleSelectProduct(){
this.selectDialogVisible=true;
this.getDialogList();
},
handleSelectSearch(){
this.getDialogList();
},
handleDialogSizeChange(val) {
this.dialogData.listQuery.pageNum = 1;
this.dialogData.listQuery.pageSize = val;
this.getDialogList();
},
handleDialogCurrentChange(val) {
this.dialogData.listQuery.pageNum = val;
this.getDialogList();
},
handleDialogSelectionChange(val){
this.dialogData.multipleSelection = val;
},
handleSelectDialogConfirm(){
if (this.dialogData.multipleSelection < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
let selectProducts = [];
for (let i = 0; i < this.dialogData.multipleSelection.length; i++) {
selectProducts.push({
productId:this.dialogData.multipleSelection[i].id,
productName:this.dialogData.multipleSelection[i].name
});
}
this.$confirm('使用要进行添加操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
createHotProduct(selectProducts).then(response=>{
this.selectDialogVisible=false;
this.dialogData.multipleSelection=[];
this.getList();
this.$message({
type: 'success',
message: '添加成功!'
});
});
});
},
handleEditSort(index,row){
this.sortDialogVisible=true;
this.sortDialogData.sort=row.sort;
this.sortDialogData.id=row.id;
},
handleUpdateSort(){
this.$confirm('是否要修改排序?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateHotProductSort(this.sortDialogData).then(response=>{
this.sortDialogVisible=false;
this.getList();
this.$message({
type: 'success',
message: '删除成功!'
});
});
})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
})
},
updateRecommendStatusStatus(ids,status){
this.$confirm('是否要修改推荐状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
params.append("recommendStatus",status);
updateRecommendStatus(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'success',
message: '已取消操作!'
});
this.getList();
});
},
deleteProduct(ids){
this.$confirm('是否要删除该推荐?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
deleteHotProduct(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '删除成功!'
});
});
})
},
getDialogList(){
fetchProductList(this.dialogData.listQuery).then(response=>{
this.dialogData.list=response.data.list;
this.dialogData.total=response.data.total;
})
}
}
}
</script>
<style></style>
#abc
<template>
<!-- 整体页面内容的外层容器用于组织和布局页面内的各个组件 -->
<div class="app-container">
<!-- 筛选搜索相关的卡片容器设置无阴影效果内部放置筛选搜索相关的元素 -->
<el-card class="filter-container" shadow="never">
<div>
<!-- 展示一个搜索图标用于直观表示此区域与搜索功能相关 -->
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<!-- 查询搜索按钮右浮动显示按钮类型为主要操作类型样式上会突出显示点击时触发 `handleSearchList` 方法用于根据设置的筛选条件发起查询搜索操作按钮尺寸为小型 -->
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<!-- 重置按钮同样右浮动且设置了右边距点击时触发 `handleResetSearch` 方法用于将筛选条件重置为默认状态按钮尺寸为小型 -->
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<!-- 内联表单用于收集筛选条件绑定的数据模型为 `listQuery`表单尺寸为小型标签宽度设置为140px -->
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<!-- 商品名称筛选的表单项目包含一个输入框通过 `v-model` 双向绑定输入框的值到 `listQuery` 中的 `productName` 属性输入框设置了特定样式类可能用于控制宽度等样式并带有提示文本 -->
<el-form-item label="商品名称:">
<el-input v-model="listQuery.productName" class="input-width" placeholder="商品名称"></el-input>
</el-form-item>
<!-- 推荐状态筛选的表单项目包含一个下拉选择框通过 `v-model` 双向绑定选择框的值到 `listQuery` 中的 `recommendStatus` 属性可清除已选选项设置了占位文本与样式类下拉选项通过循环 `recommendOptions` 数组动态生成 -->
<el-form-item label="推荐状态:">
<el-select v-model="listQuery.recommendStatus" placeholder="全部" clearable class="input-width">
<el-option v-for="item in recommendOptions"
: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>
<!-- 选择商品按钮尺寸为迷你型点击时触发 `handleSelectProduct` 方法用于打开选择商品的对话框 -->
<el-button size="mini" class="btn-add" @click="handleSelectProduct()"></el-button>
</el-card>
<!-- 表格容器用于包裹展示数据列表的表格使其在布局上与其他元素区分开来 -->
<div class="table-container">
<!-- 商品相关的数据列表表格设置了引用名称方便在JavaScript中获取实例绑定了数据来源为 `list`宽度为100%监听行选择变化事件加载状态并且显示表格边框 -->
<el-table ref="newProductTable"
:data="list"
style="width: 100%;"
@selection-change="handleSelectionChange"
v-loading="listLoading" border>
<!-- 选择列用于在表格中显示复选框方便进行多选操作宽度为60px内容居中对齐 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 编号列通过插槽获取对应行数据中的 `id` 属性值展示在单元格内宽度为120px内容居中对齐 -->
<el-table-column label="编号" width="120" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<!-- 商品名称列通过插槽获取对应行数据中的 `productName` 属性值展示在单元格内内容居中对齐 -->
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.productName}}</template>
</el-table-column>
<!-- 是否推荐列包含一个开关组件用于切换商品的推荐状态绑定了状态改变事件设置了开关的开启和关闭对应的值并且双向绑定到对应行数据中的 `recommendStatus` 属性 -->
<el-table-column label="是否推荐" width="200" align="center">
<template slot-scope="scope">
<el-switch
@change="handleRecommendStatusStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.recommendStatus">
</el-switch>
</template>
</el-table-column>
<!-- 排序列通过插槽获取对应行数据中的 `sort` 属性值展示在单元格内宽度为160px内容居中对齐 -->
<el-table-column label="排序" width="160" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<!-- 状态列通过插槽获取对应行数据中的 `recommendStatus` 属性值使用名为 `formatRecommendStatus` 的过滤器对其进行格式化处理后展示在单元格内宽度为160px内容居中对齐 -->
<el-table-column label="状态" width="160" align="center">
<template slot-scope="scope">{{scope.row.recommendStatus | formatRecommendStatus}}</template>
</el-table-column>
<!-- 操作列在单元格内放置了两个文本按钮分别用于设置排序点击触发 `handleEditSort` 方法和删除商品点击触发 `handleDelete` 方法宽度为180px内容居中对齐 -->
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleEditSort(scope.$index, scope.row)">设置排序
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 批量操作相关的容器内部放置用于选择批量操作类型的下拉框以及执行操作的按钮 -->
<div class="batch-operate-container">
<!-- 批量操作类型的下拉选择框尺寸为小型双向绑定选择的值到 `operateType` 属性带有占位提示文本下拉选项通过循环 `operates` 数组生成 -->
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
<!-- 执行批量操作的确定按钮设置了左边距添加了特定样式类按钮类型为主要操作类型尺寸为小型点击时触发 `handleBatchOperate` 方法 -->
<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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<!-- 选择商品的对话框组件设置了标题双向绑定显示状态以及宽度用于展示可选择的商品列表等相关操作 -->
<el-dialog title="选择商品" :visible.sync="selectDialogVisible" width="50%">
<!-- 输入框用于输入商品名称搜索关键字双向绑定输入的值到 `dialogData.listQuery.keyword` 属性设置了宽度外边距尺寸以及占位提示文本并且在输入框右侧有一个搜索图标按钮点击触发 `handleSelectSearch` 方法 -->
<el-input v-model="dialogData.listQuery.keyword"
style="width: 250px;margin-bottom: 20px"
size="small"
placeholder="商品名称搜索">
<el-button slot="append" icon="el-icon-search" @click="handleSelectSearch()"></el-button>
</el-input>
<!-- 展示可选择商品数据列表的表格绑定了数据来源监听行选择变化事件并且显示表格边框 -->
<el-table :data="dialogData.list"
@selection-change="handleDialogSelectionChange" border>
<!-- 选择列用于在表格中显示复选框方便多选操作宽度为60px内容居中对齐 -->
<el-table-column type="selection" width="60" align="center"></el-table-column>
<!-- 商品名称列通过插槽获取对应行数据中的 `name` 属性值展示在单元格内内容居中对齐 -->
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<!-- 货号列通过插槽获取对应行数据中的 `productSn` 属性值以特定格式添加 "NO." 前缀展示在单元格内宽度为160px内容居中对齐 -->
<el-table-column label="货号" width="160" align="center">
<template slot-scope="scope">NO.{{scope.row.productSn}}</template>
</el-table-column>
<!-- 价格列通过插槽获取对应行数据中的 `price` 属性值以特定格式添加 "¥" 前缀展示在单元格内宽度为120px内容居中对齐 -->
<el-table-column label="价格" width="120" align="center">
<template slot-scope="scope">{{scope.row.price}}</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<!-- 对话框内的分页组件用于对可选择商品列表进行分页展示同样配置了相关的事件处理方法分页布局以及绑定了页码每页显示数量可选择的每页显示数量数组和总条数等属性 -->
<el-pagination
background
@size-change="handleDialogSizeChange"
@current-change="handleDialogCurrentChange"
layout="prev, pager, next"
:current-page.sync="dialogData.listQuery.pageNum"
:page-size="dialogData.listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="dialogData.total">
</el-pagination>
</div>
<div style="clear: both;"></div>
<div slot="footer">
<!-- 取消按钮点击时隐藏对话框尺寸为小型 -->
<el-button size="small" @click="selectDialogVisible = false"> </el-button>
<!-- 确定按钮点击时触发 `handleSelectDialogConfirm` 方法尺寸为小型按钮类型为主要操作类型 -->
<el-button size="small" type="primary" @click="handleSelectDialogConfirm()"> </el-button>
</div>
</el-dialog>
<!-- 设置排序的对话框组件设置了标题双向绑定显示状态以及宽度内部包含用于输入排序值的表单 -->
<el-dialog title="设置排序"
:visible.sync="sortDialogVisible"
width="40%">
<!-- 表单用于输入排序相关的值绑定的数据模型为 `sortDialogData`标签宽度为150px -->
<el-form :model="sortDialogData"
label-width="150px">
<!-- 排序值的表单项目包含一个输入框通过 `v-model` 双向绑定输入框的值到 `sortDialogData` 中的 `sort` 属性输入框设置了宽度 -->
<el-form-item label="排序:">
<el-input v-model="sortDialogData.sort" style="width: 200px"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<!-- 取消按钮点击时隐藏对话框尺寸为小型 -->
<el-button @click="sortDialogVisible = false" size="small"> </el-button>
<!-- 确定按钮点击时触发 `handleUpdateSort` 方法尺寸为小型按钮类型为主要操作类型 -->
<el-button type="primary" @click="handleUpdateSort" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
// '@/api/hotProduct' API hotProduct
// fetchList
// updateRecommendStatus
// deleteHotProduct ID
// createHotProduct
// updateHotProductSort ID
import {fetchList, updateRecommendStatus, deleteHotProduct, createHotProduct, updateHotProductSort} from '@/api/hotProduct';
// '@/api/product' fetchList fetchProductList
import {fetchList as fetchProductList} from '@/api/product';
// defaultListQuery
// pageNum 1
// pageSize 5 5
// productName null
// recommendStatus null
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
productName: null,
recommendStatus: null
};
// defaultRecommendOptions label value
// 0 1
const defaultRecommendOptions = [
{
label: '未推荐',
value: 0
},
{
label: '推荐中',
value: 1
}
];
export default {
name: 'hotProductList',
data() {
return {
// listQuery Object.assign defaultListQuery
listQuery: Object.assign({}, defaultListQuery),
// recommendOptions defaultRecommendOptions
recommendOptions: Object.assign({}, defaultRecommendOptions),
// list null getList 便
list: null,
// total null便
total: null,
// listLoading false true false
listLoading: false,
// multipleSelection 便
multipleSelection: [],
// operates label value 012
operates: [
{
label: "设为推荐",
value: 0
},
{
label: "取消推荐",
value: 1
},
{
label: "删除",
value: 2
}
],
// operateType null value 便
operateType: null,
// selectDialogVisible false true使
selectDialogVisible: false,
// dialogData
dialogData: {
list: null,
total: null,
multipleSelection: [],
listQuery: {
keyword: null,
pageNum: 1,
pageSize: 5
}
},
// sortDialogVisible false true使便
sortDialogVisible: false,
// sortDialogData sort 0 id ID null
sortDialogData: {sort: 0, id: null}
}
},
created() {
// getList
this.getList();
},
filters: {
// formatRecommendStatus 使
// status 1 status 1 0
formatRecommendStatus(status) {
if (status === 1) {
return '推荐中';
} else {
return '未推荐';
}
}
},
methods: {
// handleResetSearch listQuery defaultListQuery 便
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
// handleSearchList
// listQuery pageNum 1 getList
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
// handleSelectionChange val multipleSelection 使 multipleSelection 便
handleSelectionChange(val) {
this.multipleSelection = val;
},
// handleSizeChange val
// listQuery pageNum 1 listQuery pageSize val getList
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// handleCurrentChange val
// listQuery pageNum val getList
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
// handleRecommendStatusStatusChange index row updateRecommendStatusStatus IDrow.idrow.recommendStatus
handleRecommendStatusStatusChange(index, row) {
this.updateRecommendStatusStatus(row.id, row.recommendStatus);
},
// handleDelete index row deleteProduct IDrow.id
handleDelete(index, row) {
this.deleteProduct(row.id);
},
// handleBatchOperate
// multipleSelection 1 warning 1000 1
// multipleSelection multipleSelection id ids
// operateType
// operateType 0 updateRecommendStatusStatus ids ID 1
// operateType 1 updateRecommendStatusStatus ids 0
// operateType 2 deleteProduct ids
// operateType 012 warning 1000
handleBatchOperate() {
if (this.multipleSelection < 1) {
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);
}
if (this.operateType === 0) {
//
this.updateRecommendStatusStatus(ids, 1);
} else if (this.operateType === 1) {
//
this.updateRecommendStatusStatus(ids, 0);
} else if (this.operateType === 2) {
//
this.updateRecommendStatusStatus(ids, 2);
} else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
}
},
// handleSelectProduct selectDialogVisible true使便 getDialogList
handleSelectProduct() {
this.selectDialogVisible = true;
this.getDialogList();
},
// handleSelectSearch getDialogList dialogData.listQuery.keyword
handleSelectSearch() {
this.getDialogList();
},
// handleDialogSizeChange val
// dialogData.listQuery pageNum 1 dialogData.listQuery pageSize val getDialogList
handleDialogSizeChange(val) {
this.dialogData.listQuery.pageNum = 1;
this.dialogData.listQuery.pageSize = val;
this.getDialogList();
},
// handleDialogCurrentChange val
// dialogData.listQuery pageNum val getDialogList

@ -0,0 +1,620 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="商品名称:">
<el-input v-model="listQuery.productName" class="input-width" placeholder="商品名称"></el-input>
</el-form-item>
<el-form-item label="推荐状态:">
<el-select v-model="listQuery.recommendStatus" placeholder="全部" clearable class="input-width">
<el-option v-for="item in recommendOptions"
: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-button size="mini" class="btn-add" @click="handleSelectProduct()"></el-button>
</el-card>
<div class="table-container">
<el-table ref="newProductTable"
: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="120" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.productName}}</template>
</el-table-column>
<el-table-column label="是否推荐" width="200" align="center">
<template slot-scope="scope">
<el-switch
@change="handleRecommendStatusStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.recommendStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column label="排序" width="160" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<el-table-column label="状态" width="160" align="center">
<template slot-scope="scope">{{scope.row.recommendStatus | formatRecommendStatus}}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleEditSort(scope.$index, scope.row)">设置排序
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
: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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<el-dialog title="选择商品" :visible.sync="selectDialogVisible" width="50%">
<el-input v-model="dialogData.listQuery.keyword"
style="width: 250px;margin-bottom: 20px"
size="small"
placeholder="商品名称搜索">
<el-button slot="append" icon="el-icon-search" @click="handleSelectSearch()"></el-button>
</el-input>
<el-table :data="dialogData.list"
@selection-change="handleDialogSelectionChange" border>
<el-table-column type="selection" width="60" align="center"></el-table-column>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="货号" width="160" align="center">
<template slot-scope="scope">NO.{{scope.row.productSn}}</template>
</el-table-column>
<el-table-column label="价格" width="120" align="center">
<template slot-scope="scope">{{scope.row.price}}</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
background
@size-change="handleDialogSizeChange"
@current-change="handleDialogCurrentChange"
layout="prev, pager, next"
:current-page.sync="dialogData.listQuery.pageNum"
:page-size="dialogData.listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="dialogData.total">
</el-pagination>
</div>
<div style="clear: both;"></div>
<div slot="footer">
<el-button size="small" @click="selectDialogVisible = false"> </el-button>
<el-button size="small" type="primary" @click="handleSelectDialogConfirm()"> </el-button>
</div>
</el-dialog>
<el-dialog title="设置排序"
:visible.sync="sortDialogVisible"
width="40%">
<el-form :model="sortDialogData"
label-width="150px">
<el-form-item label="排序:">
<el-input v-model="sortDialogData.sort" style="width: 200px"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="sortDialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleUpdateSort" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,updateRecommendStatus,deleteNewProduct,createNewProduct,updateNewProductSort} from '@/api/newProduct';
import {fetchList as fetchProductList} from '@/api/product';
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
productName: null,
recommendStatus: null
};
const defaultRecommendOptions = [
{
label: '未推荐',
value: 0
},
{
label: '推荐中',
value: 1
}
];
export default {
name: 'newProductList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
recommendOptions: Object.assign({}, defaultRecommendOptions),
list: null,
total: null,
listLoading: false,
multipleSelection: [],
operates: [
{
label: "设为推荐",
value: 0
},
{
label: "取消推荐",
value: 1
},
{
label: "删除",
value: 2
}
],
operateType: null,
selectDialogVisible:false,
dialogData:{
list: null,
total: null,
multipleSelection:[],
listQuery:{
keyword: null,
pageNum: 1,
pageSize: 5
}
},
sortDialogVisible:false,
sortDialogData:{sort:0,id:null}
}
},
created() {
this.getList();
},
filters:{
formatRecommendStatus(status){
if(status===1){
return '推荐中';
}else{
return '未推荐';
}
}
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSelectionChange(val){
this.multipleSelection = val;
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleRecommendStatusStatusChange(index,row){
this.updateRecommendStatusStatus(row.id,row.recommendStatus);
},
handleDelete(index,row){
this.deleteProduct(row.id);
},
handleBatchOperate(){
if (this.multipleSelection < 1) {
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);
}
if (this.operateType === 0) {
//
this.updateRecommendStatusStatus(ids,1);
} else if (this.operateType === 1) {
//
this.updateRecommendStatusStatus(ids,0);
} else if(this.operateType===2){
//
this.deleteProduct(ids);
}else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
}
},
handleSelectProduct(){
this.selectDialogVisible=true;
this.getDialogList();
},
handleSelectSearch(){
this.getDialogList();
},
handleDialogSizeChange(val) {
this.dialogData.listQuery.pageNum = 1;
this.dialogData.listQuery.pageSize = val;
this.getDialogList();
},
handleDialogCurrentChange(val) {
this.dialogData.listQuery.pageNum = val;
this.getDialogList();
},
handleDialogSelectionChange(val){
this.dialogData.multipleSelection = val;
},
handleSelectDialogConfirm(){
if (this.dialogData.multipleSelection < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
let selectProducts = [];
for (let i = 0; i < this.dialogData.multipleSelection.length; i++) {
selectProducts.push({
productId:this.dialogData.multipleSelection[i].id,
productName:this.dialogData.multipleSelection[i].name
});
}
this.$confirm('使用要进行添加操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
createNewProduct(selectProducts).then(response=>{
this.selectDialogVisible=false;
this.dialogData.multipleSelection=[];
this.getList();
this.$message({
type: 'success',
message: '添加成功!'
});
});
});
},
handleEditSort(index,row){
this.sortDialogVisible=true;
this.sortDialogData.sort=row.sort;
this.sortDialogData.id=row.id;
},
handleUpdateSort(){
this.$confirm('是否要修改排序?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateNewProductSort(this.sortDialogData).then(response=>{
this.sortDialogVisible=false;
this.getList();
this.$message({
type: 'success',
message: '删除成功!'
});
});
})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
})
},
updateRecommendStatusStatus(ids,status){
this.$confirm('是否要修改推荐状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
params.append("recommendStatus",status);
updateRecommendStatus(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'success',
message: '已取消操作!'
});
this.getList();
});
},
deleteProduct(ids){
this.$confirm('是否要删除该推荐?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
deleteNewProduct(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '删除成功!'
});
});
})
},
getDialogList(){
fetchProductList(this.dialogData.listQuery).then(response=>{
this.dialogData.list=response.data.list;
this.dialogData.total=response.data.total;
})
}
}
}
</script>
<style></style>
#abc
<script>
// '@/api/newProduct' API newProduct
// fetchList
// updateRecommendStatus
// deleteNewProduct ID
// createNewProduct
// updateNewProductSort ID
import {fetchList, updateRecommendStatus, deleteNewProduct, createNewProduct, updateNewProductSort} from '@/api/newProduct';
// '@/api/product' fetchList fetchProductList使
import {fetchList as fetchProductList} from '@/api/product';
// defaultListQuery
// pageNum 1便使
// pageSize 5 5
// productName null
// recommendStatus null
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
productName: null,
recommendStatus: null
};
// defaultRecommendOptions
// label value
// "" value 0 "" value 1便
const defaultRecommendOptions = [
{
label: '未推荐',
value: 0
},
{
label: '推荐中',
value: 1
}
];
export default
name: 'newProductList',
data() {
return {
// listQuery Object.assign defaultListQuery fetchList
listQuery: Object.assign({}, defaultListQuery),
// recommendOptions defaultRecommendOptions 便使
recommendOptions: Object.assign({}, defaultRecommendOptions),
// list null getList 便
list: null,
// total null便
total: null,
// listLoading false getList true false
listLoading: false,
// multipleSelection 便
multipleSelection: [],
// operates label value 012
operates: [
{
label: "设为推荐",
value: 0
},
{
label: "取消推荐",
value: 1
},
{
label: "删除",
value: 2
}
],
// operateType null value 便handleBatchOperate
operateType: null,
// selectDialogVisible false true使便
selectDialogVisible: false,
// dialogData list nulltotal nullmultipleSelection listQuery keywordpageNumpageSize
dialogData: {
list: null,
total: null,
multipleSelection: [],
listQuery: {
keyword: null,
pageNum: 1,
pageSize: 5
}
},
// sortDialogVisible false true使便
sortDialogVisible: false,
// sortDialogData sort 0 id ID null updateNewProductSort
sortDialogData: {sort: 0, id: null}
}
},
created() {
// getList
this.getList();
},
filters: {
// formatRecommendStatus 使
// status 1 status 1 0
formatRecommendStatus(status) {
if (status === 1) {
return '推荐中';
} else {
return '未推荐';
}
}
},
methods:
// handleResetSearch listQuery defaultListQuery 便使
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
// handleSearchList
// listQuery pageNum 1 getList 便
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
// handleSelectionChange val multipleSelection 使 multipleSelection 便
handleSelectionChange(val) {
this.multipleSelection = val;
},
// handleSizeChange val
// listQuery pageNum 1 listQuery pageSize val getList
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// handleCurrentChange val
// listQuery pageNum val getList
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
// handleRecommendStatusStatusChange index row updateRecommendStatusStatus IDrow.idrow.recommendStatus使
handleRecommendStatusStatusChange(index, row) {
this.updateRecommendStatusStatus(row.id, row.recommendStatus);
},
// handleDelete index row deleteProduct IDrow.id
handleDelete(index, row) {
this.deleteProduct(row.id);
},
// handleBatchOperate
// multipleSelection 1 warning 1000 1
// multipleSelection multipleSelection id ids
// operateType
// operateType 0 updateRecommendStatusStatus ids ID 1
// operateType 1 updateRecommendStatusStatus ids 0
// operateType 2 deleteProduct ids
// operateType 012 warning 1000
handleBatchOperate() {
if (this.multipleSelection < 1) {
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);
}
if (this.operateType === 0) {
//
this.updateRecommendStatusStatus(ids, 1);
} else if (this.operateType === 1) {
//
this.updateRecommendStatusStatus(ids, 0);
} else if (this.operateType === 2) {
//
this.updateRecommendStatusStatus(ids, 2);
} else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
}
},
// handleSelectProduct selectDialogVisible true使便 getDialogList
handleSelectProduct() {
this.selectDialogVisible = true;
this.getDialogList();
},
// handleSelectSearch getDialogList dialogData.listQuery.keyword 便
handleSelectSearch() {
this.getDialogList();
},

@ -0,0 +1,603 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="专题名称:">
<el-input v-model="listQuery.subjectName" class="input-width" placeholder="专题名称"></el-input>
</el-form-item>
<el-form-item label="推荐状态:">
<el-select v-model="listQuery.recommendStatus" placeholder="全部" clearable class="input-width">
<el-option v-for="item in recommendOptions"
: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-button size="mini" class="btn-add" @click="handleSelectSubject()"></el-button>
</el-card>
<div class="table-container">
<el-table ref="newSubjectTable"
: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="120" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="专题名称" align="center">
<template slot-scope="scope">{{scope.row.subjectName}}</template>
</el-table-column>
<el-table-column label="是否推荐" width="200" align="center">
<template slot-scope="scope">
<el-switch
@change="handleRecommendStatusStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.recommendStatus">
</el-switch>
</template>
</el-table-column>
<el-table-column label="排序" width="160" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<el-table-column label="状态" width="160" align="center">
<template slot-scope="scope">{{scope.row.recommendStatus | formatRecommendStatus}}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleEditSort(scope.$index, scope.row)">设置排序
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="batch-operate-container">
<el-select
size="small"
v-model="operateType" placeholder="批量操作">
<el-option
v-for="item in operates"
: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"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
<el-dialog title="选择专题" :visible.sync="selectDialogVisible" width="50%">
<el-input v-model="dialogData.listQuery.keyword"
style="width: 250px;margin-bottom: 20px"
size="small"
placeholder="专题名称搜索">
<el-button slot="append" icon="el-icon-search" @click="handleSelectSearch()"></el-button>
</el-input>
<el-table :data="dialogData.list"
@selection-change="handleDialogSelectionChange" border>
<el-table-column type="selection" width="60" align="center"></el-table-column>
<el-table-column label="专题名称" align="center">
<template slot-scope="scope">{{scope.row.title}}</template>
</el-table-column>
<el-table-column label="所属分类" width="160" align="center">
<template slot-scope="scope">{{scope.row.categoryName}}</template>
</el-table-column>
<el-table-column label="添加时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatTime}}</template>
</el-table-column>
</el-table>
<div class="pagination-container">
<el-pagination
background
@size-change="handleDialogSizeChange"
@current-change="handleDialogCurrentChange"
layout="prev, pager, next"
:current-page.sync="dialogData.listQuery.pageNum"
:page-size="dialogData.listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="dialogData.total">
</el-pagination>
</div>
<div style="clear: both;"></div>
<div slot="footer">
<el-button size="small" @click="selectDialogVisible = false"> </el-button>
<el-button size="small" type="primary" @click="handleSelectDialogConfirm()"> </el-button>
</div>
</el-dialog>
<el-dialog title="设置排序"
:visible.sync="sortDialogVisible"
width="40%">
<el-form :model="sortDialogData"
label-width="150px">
<el-form-item label="排序:">
<el-input v-model="sortDialogData.sort" style="width: 200px"></el-input>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="sortDialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleUpdateSort" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,updateRecommendStatus,deleteHomeSubject,createHomeSubject,updateHomeSubjectSort} from '@/api/homeSubject';
import {fetchList as fetchSubjectList} from '@/api/subject';
import {formatDate} from '@/utils/date';
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
subjectName: null,
recommendStatus: null
};
const defaultRecommendOptions = [
{
label: '未推荐',
value: 0
},
{
label: '推荐中',
value: 1
}
];
export default {
name: 'homeSubjectList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
recommendOptions: Object.assign({}, defaultRecommendOptions),
list: null,
total: null,
listLoading: false,
multipleSelection: [],
operates: [
{
label: "设为推荐",
value: 0
},
{
label: "取消推荐",
value: 1
},
{
label: "删除",
value: 2
}
],
operateType: null,
selectDialogVisible:false,
dialogData:{
list: null,
total: null,
multipleSelection:[],
listQuery:{
keyword: null,
pageNum: 1,
pageSize: 5
}
},
sortDialogVisible:false,
sortDialogData:{sort:0,id:null}
}
},
created() {
this.getList();
},
filters:{
formatRecommendStatus(status){
if(status===1){
return '推荐中';
}else{
return '未推荐';
}
},
formatTime(time){
if(time==null||time===''){
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSelectionChange(val){
this.multipleSelection = val;
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleRecommendStatusStatusChange(index,row){
this.updateRecommendStatusStatus(row.id,row.recommendStatus);
},
handleDelete(index,row){
this.deleteSubject(row.id);
},
handleBatchOperate(){
if (this.multipleSelection < 1) {
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);
}
if (this.operateType === 0) {
//
this.updateRecommendStatusStatus(ids,1);
} else if (this.operateType === 1) {
//
this.updateRecommendStatusStatus(ids,0);
} else if(this.operateType===2){
//
this.deleteSubject(ids);
}else {
this.$message({
message: '请选择批量操作类型',
type: 'warning',
duration: 1000
});
}
},
handleSelectSubject(){
this.selectDialogVisible=true;
this.dialogData.listQuery.keyword=null;
this.getDialogList();
},
handleSelectSearch(){
this.getDialogList();
},
handleDialogSizeChange(val) {
this.dialogData.listQuery.pageNum = 1;
this.dialogData.listQuery.pageSize = val;
this.getDialogList();
},
handleDialogCurrentChange(val) {
this.dialogData.listQuery.pageNum = val;
this.getDialogList();
},
handleDialogSelectionChange(val){
this.dialogData.multipleSelection = val;
},
handleSelectDialogConfirm(){
if (this.dialogData.multipleSelection < 1) {
this.$message({
message: '请选择一条记录',
type: 'warning',
duration: 1000
});
return;
}
let selectSubjects = [];
for (let i = 0; i < this.dialogData.multipleSelection.length; i++) {
selectSubjects.push({
subjectId:this.dialogData.multipleSelection[i].id,
subjectName:this.dialogData.multipleSelection[i].title
});
}
this.$confirm('使用要进行添加操作?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
createHomeSubject(selectSubjects).then(response=>{
this.selectDialogVisible=false;
this.dialogData.multipleSelection=[];
this.getList();
this.$message({
type: 'success',
message: '添加成功!'
});
});
});
},
handleEditSort(index,row){
this.sortDialogVisible=true;
this.sortDialogData.sort=row.sort;
this.sortDialogData.id=row.id;
},
handleUpdateSort(){
this.$confirm('是否要修改排序?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateHomeSubjectSort(this.sortDialogData).then(response=>{
this.sortDialogVisible=false;
this.getList();
this.$message({
type: 'success',
message: '删除成功!'
});
});
})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
})
},
updateRecommendStatusStatus(ids,status){
this.$confirm('是否要修改推荐状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
params.append("recommendStatus",status);
updateRecommendStatus(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'success',
message: '已取消操作!'
});
this.getList();
});
},
deleteSubject(ids){
this.$confirm('是否要删除该推荐?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params=new URLSearchParams();
params.append("ids",ids);
deleteHomeSubject(params).then(response=>{
this.getList();
this.$message({
type: 'success',
message: '删除成功!'
});
});
})
},
getDialogList(){
fetchSubjectList(this.dialogData.listQuery).then(response=>{
this.dialogData.list=response.data.list;
this.dialogData.total=response.data.total;
})
}
}
}
</script>
<style></style>
#abc
<script>
// '@/api/homeSubject' API homeSubject
// fetchList
// updateRecommendStatus
// deleteHomeSubject ID
// createHomeSubject
// updateHomeSubjectSort ID
import {fetchList, updateRecommendStatus, deleteHomeSubject, createHomeSubject, updateHomeSubjectSort} from '@/api/homeSubject';
// '@/api/subject' fetchList fetchSubjectList使
import {fetchList as fetchSubjectList} from '@/api/subject';
// '@/utils/date' formatDate 使 'yyyy-MM-dd hh:mm:ss'
import {formatDate} from '@/utils/date';
// defaultListQuery
// pageNum 1便使
// pageSize 5 5
// subjectName null
// recommendStatus null
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
subjectName: null,
recommendStatus: null
};
// defaultRecommendOptions
// label value
// "" value 0 "" value 1便
const defaultRecommendOptions = [
{
label: '未推荐',
value: 0
},
{
label: '推荐中',
value: 1
}
];
export default
name: 'homeSubjectList',
data() {
return {
// listQuery Object.assign defaultListQuery fetchList
listQuery: Object.assign({}, defaultListQuery),
// recommendOptions defaultRecommendOptions 便使
recommendOptions: Object.assign({}, defaultRecommendOptions),
// list null getList 便
list: null,
// total null便
total: null,
// listLoading false getList true false
listLoading: false,
// multipleSelection 便
multipleSelection: [],
// operates label value 012
operates: [
{
label: "设为推荐",
value: 0
},
{
label: "取消推荐",
value: 1
},
{
label: "删除",
value: 2
}
],
// operateType null value 便handleBatchOperate
operateType: null,
// selectDialogVisible false true使便
selectDialogVisible: false,
// dialogData list nulltotal nullmultipleSelection listQuery keywordpageNumpageSize
dialogData: {
list: null,
total: null,
multipleSelection: [],
listQuery: {
keyword: null,
pageNum: 1,
pageSize: 5
}
},
// sortDialogVisible false true使便
sortDialogVisible: false,
// sortDialogData sort 0 id ID null updateHomeSubjectSort
sortDialogData: {sort: 0, id: null}
}
},
created() {
// getList
this.getList();
},
filters: {
// formatRecommendStatus 使
// status 1 status 1 0
formatRecommendStatus(status) {
if (status === 1) {
return '推荐中';
} else {
return '未推荐';
}
},
// formatTime
// time null 'N/A'
// time new Date(time) formatDate 'yyyy-MM-dd hh:mm:ss' 使使
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods:
// handleResetSearch listQuery defaultListQuery 便使
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
// handleSearchList
// listQuery pageNum 1 getList 便
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
// handleSelectionChange val multipleSelection 使 multipleSelection 便
handleSelectionChange(val) {
this.multipleSelection = val;
},
// handleSizeChange val
// listQuery pageNum 1 listQuery pageSize val getList
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
// handleCurrentChange val
// listQuery pageNum val getList
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
// handleRecommendStatusStatusChange index row updateRecommendStatusStatus IDrow.idrow.recommendStatus使
handleRecommendStatusStatusChange(index, row) {
this.updateRecommendStatusStatus(row.id, row.recommendStatus);
},
// handleDelete index row deleteSubject IDrow.id
handleDelete(index, row) {
this.deleteSubject(row.id);
},
// handleBatchOperate
// multipleSelection 1 warning 1000 1
// multipleSelection multipleSelection id ids
// operateType
// operateType 0 updateRecommendStatusStatus ids ID 1
// operateType 1 updateRecommendStatusStatus ids 0
// operateType 2 deleteSubject ids
// operateType 012 warning 1000

@ -0,0 +1,14 @@
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
</style>

@ -0,0 +1,53 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/brand/list',
method:'get',
params:params
})
}
export function createBrand(data) {
return request({
url:'/brand/create',
method:'post',
data:data
})
}
export function updateShowStatus(data) {
return request({
url:'/brand/update/showStatus',
method:'post',
data:data
})
}
export function updateFactoryStatus(data) {
return request({
url:'/brand/update/factoryStatus',
method:'post',
data:data
})
}
export function deleteBrand(id) {
return request({
url:'/brand/delete/'+id,
method:'get',
})
}
export function getBrand(id) {
return request({
url:'/brand/'+id,
method:'get',
})
}
export function updateBrand(id,data) {
return request({
url:'/brand/update/'+id,
method:'post',
data:data
})
}

@ -0,0 +1,7 @@
import request from '@/utils/request'
export function fetchList() {
return request({
url:'/companyAddress/list',
method:'get'
})
}

@ -0,0 +1,38 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/coupon/list',
method:'get',
params:params
})
}
export function createCoupon(data) {
return request({
url:'/coupon/create',
method:'post',
data:data
})
}
export function getCoupon(id) {
return request({
url:'/coupon/'+id,
method:'get',
})
}
export function updateCoupon(id,data) {
return request({
url:'/coupon/update/'+id,
method:'post',
data:data
})
}
export function deleteCoupon(id) {
return request({
url:'/coupon/delete/'+id,
method:'post',
})
}

@ -0,0 +1,8 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/couponHistory/list',
method:'get',
params:params
})
}

@ -0,0 +1,35 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/flash/list',
method:'get',
params:params
})
}
export function updateStatus(id,params) {
return request({
url:'/flash/update/status/'+id,
method:'post',
params:params
})
}
export function deleteFlash(id) {
return request({
url:'/flash/delete/'+id,
method:'post'
})
}
export function createFlash(data) {
return request({
url:'/flash/create',
method:'post',
data:data
})
}
export function updateFlash(id,data) {
return request({
url:'/flash/update/'+id,
method:'post',
data:data
})
}

@ -0,0 +1,28 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/flashProductRelation/list',
method:'get',
params:params
})
}
export function createFlashProductRelation(data) {
return request({
url:'/flashProductRelation/create',
method:'post',
data:data
})
}
export function deleteFlashProductRelation(id) {
return request({
url:'/flashProductRelation/delete/'+id,
method:'post'
})
}
export function updateFlashProductRelation(id,data) {
return request({
url:'/flashProductRelation/update/'+id,
method:'post',
data:data
})
}

@ -0,0 +1,48 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url: '/flashSession/list',
method: 'get',
params: params
})
}
export function fetchSelectList(params) {
return request({
url: '/flashSession/selectList',
method: 'get',
params: params
})
}
export function updateStatus(id, params) {
return request({
url: '/flashSession/update/status/' + id,
method: 'post',
params: params
})
}
export function deleteSession(id) {
return request({
url: '/flashSession/delete/' + id,
method: 'post'
})
}
export function createSession(data) {
return request({
url: '/flashSession/create',
method: 'post',
data: data
})
}
export function updateSession(id, data) {
return request({
url: '/flashSession/update/' + id,
method: 'post',
data: data
})
}

@ -0,0 +1,43 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/home/advertise/list',
method:'get',
params:params
})
}
export function updateStatus(id,params) {
return request({
url:'/home/advertise/update/status/'+id,
method:'post',
params:params
})
}
export function deleteHomeAdvertise(data) {
return request({
url:'/home/advertise/delete',
method:'post',
data:data
})
}
export function createHomeAdvertise(data) {
return request({
url:'/home/advertise/create',
method:'post',
data:data
})
}
export function getHomeAdvertise(id) {
return request({
url:'/home/advertise/'+id,
method:'get',
})
}
export function updateHomeAdvertise(id,data) {
return request({
url:'/home/advertise/update/'+id,
method:'post',
data:data
})
}

@ -0,0 +1,40 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/home/brand/list',
method:'get',
params:params
})
}
export function updateRecommendStatus(data) {
return request({
url:'/home/brand/update/recommendStatus',
method:'post',
data:data
})
}
export function deleteHomeBrand(data) {
return request({
url:'/home/brand/delete',
method:'post',
data:data
})
}
export function createHomeBrand(data) {
return request({
url:'/home/brand/create',
method:'post',
data:data
})
}
export function updateHomeBrandSort(params) {
return request({
url:'/home/brand/update/sort/'+params.id,
method:'post',
params:params
})
}

@ -0,0 +1,40 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/home/recommendSubject/list',
method:'get',
params:params
})
}
export function updateRecommendStatus(data) {
return request({
url:'/home/recommendSubject/update/recommendStatus',
method:'post',
data:data
})
}
export function deleteHomeSubject(data) {
return request({
url:'/home/recommendSubject/delete',
method:'post',
data:data
})
}
export function createHomeSubject(data) {
return request({
url:'/home/recommendSubject/create',
method:'post',
data:data
})
}
export function updateHomeSubjectSort(params) {
return request({
url:'/home/recommendSubject/update/sort/'+params.id,
method:'post',
params:params
})
}

@ -0,0 +1,40 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/home/recommendProduct/list',
method:'get',
params:params
})
}
export function updateRecommendStatus(data) {
return request({
url:'/home/recommendProduct/update/recommendStatus',
method:'post',
data:data
})
}
export function deleteHotProduct(data) {
return request({
url:'/home/recommendProduct/delete',
method:'post',
data:data
})
}
export function createHotProduct(data) {
return request({
url:'/home/recommendProduct/create',
method:'post',
data:data
})
}
export function updateHotProductSort(params) {
return request({
url:'/home/recommendProduct/update/sort/'+params.id,
method:'post',
params:params
})
}

@ -0,0 +1,80 @@
import request from '@/utils/request'
export function login(username, password) {
return request({
url: '/admin/login',
method: 'post',
data: {
username,
password
}
})
}
export function getInfo() {
return request({
url: '/admin/info',
method: 'get',
})
}
export function logout() {
return request({
url: '/admin/logout',
method: 'post'
})
}
export function fetchList(params) {
return request({
url: '/admin/list',
method: 'get',
params: params
})
}
export function createAdmin(data) {
return request({
url: '/admin/register',
method: 'post',
data: data
})
}
export function updateAdmin(id, data) {
return request({
url: '/admin/update/' + id,
method: 'post',
data: data
})
}
export function updateStatus(id, params) {
return request({
url: '/admin/updateStatus/' + id,
method: 'post',
params: params
})
}
export function deleteAdmin(id) {
return request({
url: '/admin/delete/' + id,
method: 'post'
})
}
export function getRoleByAdmin(id) {
return request({
url: '/admin/role/' + id,
method: 'get'
})
}
export function allocRole(data) {
return request({
url: '/admin/role/update',
method: 'post',
data: data
})
}

@ -0,0 +1,8 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/memberLevel/list',
method:'get',
params:params
})
}

@ -0,0 +1,55 @@
import request from '@/utils/request'
export function fetchList(parentId, params) {
return request({
url: '/menu/list/' + parentId,
method: 'get',
params: params
})
}
export function deleteMenu(id) {
return request({
url: '/menu/delete/' + id,
method: 'post'
})
}
export function createMenu(data) {
return request({
url: '/menu/create',
method: 'post',
data: data
})
}
export function updateMenu(id, data) {
return request({
url: '/menu/update/' + id,
method: 'post',
data: data
})
}
export function getMenu(id) {
return request({
url: '/menu/' + id,
method: 'get',
})
}
export function updateHidden(id, params) {
return request({
url: '/menu/updateHidden/' + id,
method: 'post',
params: params
})
}
export function fetchTreeList() {
return request({
url: '/menu/treeList',
method: 'get'
})
}

@ -0,0 +1,40 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/home/newProduct/list',
method:'get',
params:params
})
}
export function updateRecommendStatus(data) {
return request({
url:'/home/newProduct/update/recommendStatus',
method:'post',
data:data
})
}
export function deleteNewProduct(data) {
return request({
url:'/home/newProduct/delete',
method:'post',
data:data
})
}
export function createNewProduct(data) {
return request({
url:'/home/newProduct/create',
method:'post',
data:data
})
}
export function updateNewProductSort(params) {
return request({
url:'/home/newProduct/update/sort/'+params.id,
method:'post',
params:params
})
}

@ -0,0 +1,63 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/order/list',
method:'get',
params:params
})
}
export function closeOrder(params) {
return request({
url:'/order/update/close',
method:'post',
params:params
})
}
export function deleteOrder(params) {
return request({
url:'/order/delete',
method:'post',
params:params
})
}
export function deliveryOrder(data) {
return request({
url:'/order/update/delivery',
method:'post',
data:data
});
}
export function getOrderDetail(id) {
return request({
url:'/order/'+id,
method:'get'
});
}
export function updateReceiverInfo(data) {
return request({
url:'/order/update/receiverInfo',
method:'post',
data:data
});
}
export function updateMoneyInfo(data) {
return request({
url:'/order/update/moneyInfo',
method:'post',
data:data
});
}
export function updateOrderNote(params) {
return request({
url:'/order/update/note',
method:'post',
params:params
})
}

@ -0,0 +1,15 @@
import request from '@/utils/request'
export function getOrderSetting(id) {
return request({
url:'/orderSetting/'+id,
method:'get',
})
}
export function updateOrderSetting(id,data) {
return request({
url:'/orderSetting/update/'+id,
method:'post',
data:data
})
}

@ -0,0 +1,7 @@
import request from '@/utils/request'
export function policy() {
return request({
url:'/aliyun/oss/policy',
method:'get',
})
}

@ -0,0 +1,7 @@
import request from '@/utils/request'
export function fetchList() {
return request({
url:'/prefrenceArea/listAll',
method:'get',
})
}

@ -0,0 +1,72 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/product/list',
method:'get',
params:params
})
}
export function fetchSimpleList(params) {
return request({
url:'/product/simpleList',
method:'get',
params:params
})
}
export function updateDeleteStatus(params) {
return request({
url:'/product/update/deleteStatus',
method:'post',
params:params
})
}
export function updateNewStatus(params) {
return request({
url:'/product/update/newStatus',
method:'post',
params:params
})
}
export function updateRecommendStatus(params) {
return request({
url:'/product/update/recommendStatus',
method:'post',
params:params
})
}
export function updatePublishStatus(params) {
return request({
url:'/product/update/publishStatus',
method:'post',
params:params
})
}
export function createProduct(data) {
return request({
url:'/product/create',
method:'post',
data:data
})
}
export function updateProduct(id,data) {
return request({
url:'/product/update/'+id,
method:'post',
data:data
})
}
export function getProduct(id) {
return request({
url:'/product/updateInfo/'+id,
method:'get',
})
}

@ -0,0 +1,45 @@
import request from '@/utils/request'
export function fetchList(cid,params) {
return request({
url:'/productAttribute/list/'+cid,
method:'get',
params:params
})
}
export function deleteProductAttr(data) {
return request({
url:'/productAttribute/delete',
method:'post',
data:data
})
}
export function createProductAttr(data) {
return request({
url:'/productAttribute/create',
method:'post',
data:data
})
}
export function updateProductAttr(id,data) {
return request({
url:'/productAttribute/update/'+id,
method:'post',
data:data
})
}
export function getProductAttr(id) {
return request({
url:'/productAttribute/'+id,
method:'get'
})
}
export function getProductAttrInfo(productCategoryId) {
return request({
url:'/productAttribute/attrInfo/'+productCategoryId,
method:'get'
})
}

@ -0,0 +1,37 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/productAttribute/category/list',
method:'get',
params:params
})
}
export function createProductAttrCate(data) {
return request({
url:'/productAttribute/category/create',
method:'post',
data:data
})
}
export function deleteProductAttrCate(id) {
return request({
url:'/productAttribute/category/delete/'+id,
method:'get'
})
}
export function updateProductAttrCate(id,data) {
return request({
url:'/productAttribute/category/update/'+id,
method:'post',
data:data
})
}
export function fetchListWithAttr() {
return request({
url:'/productAttribute/category/list/withAttr',
method:'get'
})
}

@ -0,0 +1,60 @@
import request from '@/utils/request'
export function fetchList(parentId,params) {
return request({
url:'/productCategory/list/'+parentId,
method:'get',
params:params
})
}
export function deleteProductCate(id) {
return request({
url:'/productCategory/delete/'+id,
method:'post'
})
}
export function createProductCate(data) {
return request({
url:'/productCategory/create',
method:'post',
data:data
})
}
export function updateProductCate(id,data) {
return request({
url:'/productCategory/update/'+id,
method:'post',
data:data
})
}
export function getProductCate(id) {
return request({
url:'/productCategory/'+id,
method:'get',
})
}
export function updateShowStatus(data) {
return request({
url:'/productCategory/update/showStatus',
method:'post',
data:data
})
}
export function updateNavStatus(data) {
return request({
url:'/productCategory/update/navStatus',
method:'post',
data:data
})
}
export function fetchListWithChildren() {
return request({
url:'/productCategory/list/withChildren',
method:'get'
})
}

@ -0,0 +1,39 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url: '/resource/list',
method: 'get',
params: params
})
}
export function createResource(data) {
return request({
url: '/resource/create',
method: 'post',
data: data
})
}
export function updateResource(id, data) {
return request({
url: '/resource/update/' + id,
method: 'post',
data: data
})
}
export function deleteResource(id) {
return request({
url: '/resource/delete/' + id,
method: 'post'
})
}
export function fetchAllResourceList() {
return request({
url: '/resource/listAll',
method: 'get'
})
}

@ -0,0 +1,31 @@
import request from '@/utils/request'
export function listAllCate() {
return request({
url: '/resourceCategory/listAll',
method: 'get'
})
}
export function createResourceCategory(data) {
return request({
url: '/resourceCategory/create',
method: 'post',
data: data
})
}
export function updateResourceCategory(id, data) {
return request({
url: '/resourceCategory/update/' + id,
method: 'post',
data: data
})
}
export function deleteResourceCategory(id) {
return request({
url: '/resourceCategory/delete/' + id,
method: 'post'
})
}

@ -0,0 +1,30 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/returnApply/list',
method:'get',
params:params
})
}
export function deleteApply(params) {
return request({
url:'/returnApply/delete',
method:'post',
params:params
})
}
export function updateApplyStatus(id,data) {
return request({
url:'/returnApply/update/status/'+id,
method:'post',
data:data
})
}
export function getApplyDetail(id) {
return request({
url:'/returnApply/'+id,
method:'get'
})
}

@ -0,0 +1,47 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url:'/returnReason/list',
method:'get',
params:params
})
}
export function deleteReason(params) {
return request({
url:'/returnReason/delete',
method:'post',
params:params
})
}
export function updateStatus(params) {
return request({
url:'/returnReason/update/status',
method:'post',
params:params
})
}
export function addReason(data) {
return request({
url:'/returnReason/create',
method:'post',
data:data
})
}
export function getReasonDetail(id) {
return request({
url:'/returnReason/'+id,
method:'get'
})
}
export function updateReason(id,data) {
return request({
url:'/returnReason/update/'+id,
method:'post',
data:data
})
}

@ -0,0 +1,78 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url: '/role/list',
method: 'get',
params: params
})
}
export function createRole(data) {
return request({
url: '/role/create',
method: 'post',
data: data
})
}
export function updateRole(id, data) {
return request({
url: '/role/update/' + id,
method: 'post',
data: data
})
}
export function updateStatus(id, params) {
return request({
url: '/role/updateStatus/' + id,
method: 'post',
params: params
})
}
export function deleteRole(data) {
return request({
url:'/role/delete',
method:'post',
data:data
})
}
export function fetchAllRoleList() {
return request({
url: '/role/listAll',
method: 'get'
})
}
export function listMenuByRole(roleId) {
return request({
url: '/role/listMenu/'+roleId,
method: 'get'
})
}
export function listResourceByRole(roleId) {
return request({
url: '/role/listResource/'+roleId,
method: 'get'
})
}
export function allocMenu(data) {
return request({
url: '/role/allocMenu',
method: 'post',
data:data
})
}
export function allocResource(data) {
return request({
url: '/role/allocResource',
method: 'post',
data:data
})
}

@ -0,0 +1,16 @@
import request from '@/utils/request'
export function fetchList(pid,params) {
return request({
url:'/sku/'+pid,
method:'get',
params:params
})
}
export function update(pid,data) {
return request({
url:'/sku/update/'+pid,
method:'post',
data:data
})
}

@ -0,0 +1,15 @@
import request from '@/utils/request'
export function fetchListAll() {
return request({
url:'/subject/listAll',
method:'get',
})
}
export function fetchList(params) {
return request({
url:'/subject/list',
method:'get',
params:params
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save