Merge pull request #1 from linlinjava/master

update
This commit is contained in:
端午安康
2020-08-04 10:20:46 +08:00
committed by GitHub
72 changed files with 769 additions and 382 deletions

View File

@@ -6,7 +6,7 @@ spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
driver-class-name: com.mysql.cj.jdbc.Driver
username: litemall
password: litemall123456
initial-size: 10

View File

@@ -16,10 +16,16 @@ cat $LITEMALL_HOME/litemall-db/sql/litemall_schema.sql > $LITEMALL_HOME/deploy/d
cat $LITEMALL_HOME/litemall-db/sql/litemall_table.sql >> $LITEMALL_HOME/deploy/db/litemall.sql
cat $LITEMALL_HOME/litemall-db/sql/litemall_data.sql >> $LITEMALL_HOME/deploy/db/litemall.sql
cd $LITEMALL_HOME/litemall-admin
# 安装阿里node镜像工具
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 安装node项目依赖环境
# 打包litemall-admin
cd $LITEMALL_HOME/litemall-admin
cnpm install
cnpm run build:dep
# 打包litemall-vue
cd $LITEMALL_HOME/litemall-vue
cnpm install
cnpm run build:dep

View File

@@ -8,7 +8,7 @@
* vue-router
* axios
* element
* vue-element-admin 4.2.1
* vue-element-admin 4.3.0
* 其他见package.json
* 管理后台后端, 即litemall-admin-api模块
* Spring Boot 2.x
@@ -18,9 +18,6 @@
* `缺失`首页中实现一些小组件,同时点击能够跳转相应页面
* `缺失`支持导出表所有数据
* `改善`管理员登录页面打开慢,优化速度
* `改善`地址优化,目前每一次点击都会请求后台,应该缓存已有的数据
* `改善`vue和vue-element-admin等及时更新
## 4.1 litemall-admin-api

View File

@@ -1339,11 +1339,11 @@ litemall-admin编译得到的前端文件在第一次加载时相当耗时
这里deploy部署方式比较简单不灵活开发者可以参考开发自己的项目脚本。
#### 1.7.2.2 .gitlab-ci.yml部署
#### 1.7.2.2 docker部署
目前不支持
当前项目存在docker部署文件夹这个是上述1.5.1节部署腾讯云服务器所采取的一些脚本。
#### 1.7.2.3 docker部署
#### 1.7.2.3 .gitlab-ci.yml部署
目前不支持

View File

@@ -5,8 +5,8 @@ spring:
encoding: UTF-8
datasource:
druid:
url: jdbc:mysql://mysql:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://mysql:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: litemall
password: litemall123456
initial-size: 10

View File

@@ -16,10 +16,16 @@ cat $LITEMALL_HOME/litemall-db/sql/litemall_schema.sql > $LITEMALL_HOME/docker/d
cat $LITEMALL_HOME/litemall-db/sql/litemall_table.sql >> $LITEMALL_HOME/docker/db/init-sql/litemall.sql
cat $LITEMALL_HOME/litemall-db/sql/litemall_data.sql >> $LITEMALL_HOME/docker/db/init-sql/litemall.sql
cd $LITEMALL_HOME/litemall-admin
# 安装阿里node镜像工具
npm install -g cnpm --registry=https://registry.npm.taobao.org
# 安装node项目依赖环境
# 打包litemall-admin
cd $LITEMALL_HOME/litemall-admin
cnpm install
cnpm run build:dep
# 打包litemall-vue
cd $LITEMALL_HOME/litemall-vue
cnpm install
cnpm run build:dep

View File

@@ -12,12 +12,16 @@ import org.linlinjava.litemall.db.service.LitemallUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import javax.validation.constraints.NotNull;
@RestController
@RequestMapping("/admin/user")
@Validated
@@ -38,4 +42,17 @@ public class AdminUserController {
List<LitemallUser> userList = userService.querySelective(username, mobile, page, limit, sort, order);
return ResponseUtil.okList(userList);
}
@RequiresPermissions("admin:user:list")
@RequiresPermissionsDesc(menu = {"用户管理", "会员管理"}, button = "详情")
@GetMapping("/detail")
public Object userDetail(@NotNull Integer id) {
LitemallUser user=userService.findById(id);
return ResponseUtil.ok(user);
}
@RequiresPermissions("admin:user:list")
@RequiresPermissionsDesc(menu = {"用户管理", "会员管理"}, button = "编辑")
@PostMapping("/update")
public Object userUpdate(@RequestBody LitemallUser user) {
return ResponseUtil.ok(userService.updateById(user));
}
}

View File

@@ -1,7 +1,7 @@
{
"name": "litemall-admin",
"version": "1.0.0",
"description": "litemall-admin basing on vue-element-admin 4.2.1",
"description": "litemall-admin basing on vue-element-admin 4.3.0",
"author": "linlinjava <linlinjava@163.com>",
"license": "MIT",
"scripts": {
@@ -50,13 +50,14 @@
"clipboard": "2.0.4",
"connect": "3.6.6",
"echarts": "4.2.1",
"element-ui": "2.12.0",
"element-ui": "2.13.2",
"file-saver": "1.3.8",
"js-cookie": "2.2.0",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"screenfull": "4.2.0",
"script-loader": "0.7.2",
"vue": "2.6.10",
"vue-count-to": "1.0.13",
"vue-router": "3.0.2",
@@ -83,11 +84,10 @@
"html-webpack-plugin": "3.2.0",
"husky": "1.3.1",
"lint-staged": "8.1.5",
"node-sass": "^4.9.0",
"sass": "^1.26.2",
"runjs": "^4.3.2",
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",
"serve-static": "^1.13.2",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.0",

View File

@@ -8,6 +8,22 @@ export function fetchList(query) {
})
}
export function userDetail(id) {
return request({
url: '/user/detail',
method: 'get',
params: {id}
})
}
export function updateUser(data) {
return request({
url: '/user/update',
method: 'post',
data
})
}
export function listAddress(query) {
return request({
url: '/address/list',
@@ -47,4 +63,3 @@ export function listHistory(query) {
params: query
})
}

View File

@@ -42,7 +42,7 @@ export default {
</script>
<style lang="scss" scoped>
/deep/ .el-badge__content.is-fixed.is-dot {
::v-deep .el-badge__content.is-fixed.is-dot {
right: 5px;
top: 10px;
}

View File

@@ -1,6 +1,6 @@
<template>
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
<slot/>
<slot />
</el-scrollbar>
</template>
@@ -80,7 +80,7 @@ export default {
position: relative;
overflow: hidden;
width: 100%;
/deep/ {
::v-deep {
.el-scrollbar__bar {
bottom: 0px;
}

View File

@@ -28,7 +28,7 @@ router.beforeEach((to, from, next) => {
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
const perms = res.data.data.perms // note: perms must be a array! such as: ['GET /aaa','POST /bbb']
store.dispatch('GenerateRoutes', { perms }).then(() => { // 根据perms权限生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
router.addRoutes(store.getters.addRoutes) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {

View File

@@ -24,14 +24,14 @@ import Layout from '@/views/layout/Layout'
noCache: true if true ,the page will no be cached(default is false)
}
**/
export const constantRouterMap = [
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path*',
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index')
}
]
@@ -71,13 +71,7 @@ export const constantRouterMap = [
}
]
export default new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})
export const asyncRouterMap = [
export const asyncRoutes = [
{
path: '/user',
component: Layout,
@@ -612,3 +606,19 @@ export const asyncRouterMap = [
{ path: '*', redirect: '/404', hidden: true }
]
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})
const router = createRouter()
// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
const newRouter = createRouter()
router.matcher = newRouter.matcher // reset router
}
export default router

View File

@@ -8,12 +8,9 @@ const getters = {
token: state => state.user.token,
avatar: state => state.user.avatar,
name: state => state.user.name,
introduction: state => state.user.introduction,
status: state => state.user.status,
roles: state => state.user.roles,
perms: state => state.user.perms,
setting: state => state.user.setting,
permission_routers: state => state.permission.routers,
addRouters: state => state.permission.addRouters
permission_routes: state => state.permission.routes,
addRoutes: state => state.permission.addRoutes
}
export default getters

View File

@@ -1,4 +1,4 @@
import { asyncRouterMap, constantRouterMap } from '@/router'
import { asyncRoutes, constantRoutes } from '@/router'
/**
* 通过meta.perms判断是否与当前用户权限匹配
@@ -15,16 +15,16 @@ function hasPermission(perms, route) {
/**
* 递归过滤异步路由表,返回符合用户角色权限的路由表
* @param routes asyncRouterMap
* @param routes asyncRoutes
* @param perms
*/
function filterAsyncRouter(routes, perms) {
function filterAsyncRoutes(routes, perms) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (tmp.children) {
tmp.children = filterAsyncRouter(tmp.children, perms)
tmp.children = filterAsyncRoutes(tmp.children, perms)
if (tmp.children && tmp.children.length > 0) {
res.push(tmp)
}
@@ -40,26 +40,26 @@ function filterAsyncRouter(routes, perms) {
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
routes: constantRoutes,
addRoutes: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
},
actions: {
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
const { perms } = data
let accessedRouters
let accessedRoutes
if (perms.includes('*')) {
accessedRouters = asyncRouterMap
accessedRoutes = asyncRoutes
} else {
accessedRouters = filterAsyncRouter(asyncRouterMap, perms)
accessedRoutes = filterAsyncRoutes(asyncRoutes, perms)
}
commit('SET_ROUTERS', accessedRouters)
commit('SET_ROUTES', accessedRoutes)
resolve()
})
}

View File

@@ -1,38 +1,21 @@
import { loginByUsername, logout, getUserInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import router, { resetRouter } from '@/router'
const user = {
state: {
user: '',
status: '',
code: '',
token: getToken(),
name: '',
avatar: '',
introduction: '',
roles: [],
perms: [],
setting: {
articlePlatform: []
}
perms: []
},
mutations: {
SET_CODE: (state, code) => {
state.code = code
},
SET_TOKEN: (state, token) => {
state.token = token
},
SET_INTRODUCTION: (state, introduction) => {
state.introduction = introduction
},
SET_SETTING: (state, setting) => {
state.setting = setting
},
SET_STATUS: (state, status) => {
state.status = status
},
SET_NAME: (state, name) => {
state.name = name
},
@@ -78,7 +61,6 @@ const user = {
commit('SET_ROLES', data.roles)
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction)
resolve(response)
}).catch(error => {
reject(error)
@@ -86,28 +68,20 @@ const user = {
})
},
// 第三方验证登录
// LoginByThirdparty({ commit, state }, code) {
// return new Promise((resolve, reject) => {
// commit('SET_CODE', code)
// loginByThirdparty(state.status, state.email, state.code).then(response => {
// commit('SET_TOKEN', response.data.token)
// setToken(response.data.token)
// resolve()
// }).catch(error => {
// reject(error)
// })
// })
// },
// 登出
LogOut({ commit, state }) {
LogOut({ commit, state, dispatch }) {
return new Promise((resolve, reject) => {
logout(state.token).then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
commit('SET_PERMS', [])
removeToken()
resetRouter()
// reset visited views and cached views
// to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485
dispatch('tagsView/delAllViews', null, { root: true })
resolve()
}).catch(error => {
reject(error)
@@ -119,6 +93,7 @@ const user = {
FedLogOut({ commit }) {
return new Promise(resolve => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
})
@@ -126,19 +101,23 @@ const user = {
// 动态修改权限
ChangeRoles({ commit, dispatch }, role) {
return new Promise(resolve => {
return new Promise(async resolve => {
commit('SET_TOKEN', role)
setToken(role)
getUserInfo(role).then(response => {
const data = response.data
commit('SET_ROLES', data.roles)
commit('SET_PERMS', data.perms)
commit('SET_NAME', data.name)
commit('SET_AVATAR', data.avatar)
commit('SET_INTRODUCTION', data.introduction)
dispatch('GenerateRoutes', data) // 动态修改权限后 重绘侧边菜单
resolve()
})
const { roles } = await dispatch('GetUserInfo')
resetRouter()
const accessRoutes = await dispatch('permission/generateRoutes', roles, { root: true })
// dynamically add accessible routes
router.addRoutes(accessRoutes)
// reset visited views and cached views
dispatch('tagsView/delAllViews', null, { root: true })
resolve()
})
}
}

View File

@@ -31,7 +31,7 @@
.fixed-width {
.el-button--mini {
padding: 7px 10px;
width: 60px;
min-width: 60px;
}
}

View File

@@ -6,7 +6,7 @@
/* theme color */
$--color-primary: #1890ff;
$--color-success: #13ce66;
$--color-warning: #FFBA00;
$--color-warning: #ffba00;
$--color-danger: #ff4949;
// $--color-info: #1E1E1E;
@@ -17,10 +17,10 @@ $--button-font-weight: 400;
$--border-color-light: #dfe4ed;
$--border-color-lighter: #e6ebf5;
$--table-border:1px solid#dfe6ec;
$--table-border: 1px solid #dfe6ec;
/* icon font path, required */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
$--font-path: "~element-ui/lib/theme-chalk/fonts";
@import "~element-ui/packages/theme-chalk/src/index";

View File

@@ -57,6 +57,11 @@
margin-right: 16px;
}
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
.el-menu {
border: none;
height: 100%;
@@ -105,6 +110,11 @@
.svg-icon {
margin-left: 20px;
}
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
}
}
@@ -118,6 +128,11 @@
margin-left: 20px;
}
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
.el-submenu__icon-arrow {
display: none;
}
@@ -178,6 +193,10 @@
.svg-icon {
margin-right: 16px;
}
.sub-el-icon {
margin-right: 12px;
margin-left: -2px;
}
}
.nest-menu .el-submenu>.el-submenu__title,

View File

@@ -1,5 +1,5 @@
/* eslint-disable */
require('script-loader!file-saver');
import { saveAs } from 'file-saver'
import XLSX from 'xlsx'
function generateArray(table) {
@@ -145,20 +145,34 @@ export function export_table_to_excel(id) {
}
export function export_json_to_excel({
multiHeader = [],
header,
data,
filename,
merges = [],
autoWidth = true,
bookType= 'xlsx'
bookType = 'xlsx'
} = {}) {
/* original data */
filename = filename || 'excel-list'
data = [...data]
data.unshift(header);
for (let i = multiHeader.length - 1; i > -1; i--) {
data.unshift(multiHeader[i])
}
var ws_name = "SheetJS";
var wb = new Workbook(),
ws = sheet_from_array_of_arrays(data);
if (merges.length > 0) {
if (!ws['!merges']) ws['!merges'] = [];
merges.forEach(item => {
ws['!merges'].push(XLSX.utils.decode_range(item))
})
}
if (autoWidth) {
/*设置worksheet每列的最大宽度*/
const colWidth = data.map(row => row.map(val => {

View File

@@ -3,8 +3,8 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.userId" clearable class="filter-item" style="width: 200px;" placeholder="请输入用户ID"/>
<el-input v-model="listQuery.valueId" clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID"/>
<el-input v-model="listQuery.userId" clearable class="filter-item" style="width: 200px;" placeholder="请输入用户ID" />
<el-input v-model="listQuery.valueId" clearable class="filter-item" style="width: 200px;" placeholder="请输入商品ID" />
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
</div>
@@ -12,21 +12,21 @@
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" label="用户ID" prop="userId"/>
<el-table-column align="center" label="用户ID" prop="userId" />
<el-table-column align="center" label="商品ID" prop="valueId"/>
<el-table-column align="center" label="商品ID" prop="valueId" />
<el-table-column align="center" label="打分" prop="star"/>
<el-table-column align="center" label="打分" prop="star" />
<el-table-column align="center" label="评论内容" prop="content"/>
<el-table-column align="center" label="评论内容" prop="content" />
<el-table-column align="center" label="评论图片" prop="picUrls">
<template slot-scope="scope">
<el-image v-for="item in scope.row.picUrls" :key="item" :src="item" :preview-src-list="scope.row.picUrls" :lazy="true" style="width: 40px; height: 40px; margin-right: 5px;"/>
<el-image v-for="item in scope.row.picUrls" :key="item" :src="item" :preview-src-list="scope.row.picUrls" :lazy="true" style="width: 40px; height: 40px; margin-right: 5px;" />
</template>
</el-table-column>
<el-table-column align="center" label="时间" prop="addTime"/>
<el-table-column align="center" label="时间" prop="addTime" />
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
@@ -42,7 +42,7 @@
<el-dialog :visible.sync="replyFormVisible" title="回复">
<el-form ref="replyForm" :model="replyForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="回复内容" prop="content">
<el-input :autosize="{ minRows: 4, maxRows: 8}" v-model="replyForm.content" type="textarea"/>
<el-input v-model="replyForm.content" :autosize="{ minRows: 4, maxRows: 8}" type="textarea" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -129,8 +129,7 @@ export default {
type: 'success',
duration: 2000
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
},
handleDownload() {

View File

@@ -410,7 +410,8 @@ export default {
this.goods.categoryId = value[value.length - 1]
},
handleCancel: function() {
this.$router.push({ path: '/goods/goods' })
this.$store.dispatch('tagsView/delView', this.$route)
this.$router.push({ path: '/goods/list' })
},
handlePublish: function() {
const finalGoods = {
@@ -424,6 +425,7 @@ export default {
title: '成功',
message: '创建成功'
})
this.$store.dispatch('tagsView/delView', this.$route)
this.$router.push({ path: '/goods/list' })
}).catch(response => {
MessageBox.alert('业务错误:' + response.data.errmsg, '警告', {

View File

@@ -419,6 +419,7 @@ export default {
this.goods.categoryId = value[value.length - 1]
},
handleCancel: function() {
this.$store.dispatch('tagsView/delView', this.$route)
this.$router.push({ path: '/goods/list' })
},
handleEdit: function() {
@@ -432,8 +433,9 @@ export default {
.then(response => {
this.$notify.success({
title: '成功',
message: '创建成功'
message: '编辑成功'
})
this.$store.dispatch('tagsView/delView', this.$route)
this.$router.push({ path: '/goods/list' })
})
.catch(response => {

View File

@@ -190,8 +190,7 @@ export default {
title: '成功',
message: '删除成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
}).catch(response => {
this.$notify.error({
title: '失败',

View File

@@ -46,8 +46,8 @@
密码修改
</router-link>
</el-dropdown-item>
<el-dropdown-item divided>
<span style="display:block;" @click="logout">退出</span>
<el-dropdown-item divided @click.native="logout">
<span style="display:block;">退出</span>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>

View File

@@ -17,7 +17,11 @@ export default {
const vnodes = []
if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>)
if (icon.includes('el-icon')) {
vnodes.push(<i class={[icon, 'sub-el-icon']} />)
} else {
vnodes.push(<svg-icon icon-class={icon}/>)
}
}
if (title) {
@@ -27,3 +31,11 @@ export default {
}
}
</script>
<style scoped>
.sub-el-icon {
color: currentColor;
width: 1em;
height: 1em;
}
</style>

View File

@@ -1,14 +1,11 @@
<template>
<!-- eslint-disable vue/require-component-is-->
<component v-bind="linkProps(to)">
<slot/>
<component :is="type" v-bind="linkProps(to)">
<slot />
</component>
</template>
<script>
import { isExternal } from '@/utils'
import { isExternal } from '@/utils/validate'
export default {
props: {
to: {
@@ -16,22 +13,28 @@ export default {
required: true
}
},
methods: {
isExternalLink(routePath) {
return isExternal(routePath)
computed: {
isExternal() {
return isExternal(this.to)
},
linkProps(url) {
if (this.isExternalLink(url)) {
type() {
if (this.isExternal) {
return 'a'
}
return 'router-link'
}
},
methods: {
linkProps(to) {
if (this.isExternal) {
return {
is: 'a',
href: url,
href: to,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'router-link',
to: url
to: to
}
}
}

View File

@@ -9,7 +9,7 @@
text-color="#bfcbd9"
active-text-color="#409EFF"
>
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/>
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
</template>
@@ -22,7 +22,7 @@ export default {
components: { SidebarItem },
computed: {
...mapGetters([
'permission_routers',
'permission_routes',
'sidebar'
]),
isCollapse() {

View File

@@ -6,7 +6,6 @@
<script>
const tagAndTagSpacing = 4 // tagAndTagSpacing
export default {
name: 'ScrollPane',
data() {
@@ -19,27 +18,33 @@ export default {
return this.$refs.scrollContainer.$refs.wrap
}
},
mounted() {
this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
},
beforeDestroy() {
this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 40
const $scrollWrapper = this.scrollWrapper
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
},
emitScroll() {
this.$emit('scroll')
},
moveToTarget(currentTag) {
const $container = this.$refs.scrollContainer.$el
const $containerWidth = $container.offsetWidth
const $scrollWrapper = this.scrollWrapper
const tagList = this.$parent.$refs.tag
let firstTag = null
let lastTag = null
// find first tag and last tag
if (tagList.length > 0) {
firstTag = tagList[0]
lastTag = tagList[tagList.length - 1]
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0
} else if (lastTag === currentTag) {
@@ -49,13 +54,10 @@ export default {
const currentIndex = tagList.findIndex(item => item === currentTag)
const prevTag = tagList[currentIndex - 1]
const nextTag = tagList[currentIndex + 1]
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
@@ -73,7 +75,7 @@ export default {
position: relative;
overflow: hidden;
width: 100%;
/deep/ {
::v-deep {
.el-scrollbar__bar {
bottom: 0px;
}

View File

@@ -1,6 +1,6 @@
<template>
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
<router-link
v-for="tag in visitedViews"
ref="tag"
@@ -17,10 +17,10 @@
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">刷新</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">关闭</li>
<li @click="closeOthersTags">关闭其他</li>
<li @click="closeAllTags(selectedTag)">关闭所有</li>
<li @click="refreshSelectedTag(selectedTag)">Refresh</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">Close</li>
<li @click="closeOthersTags">Close Others</li>
<li @click="closeAllTags(selectedTag)">Close All</li>
</ul>
</div>
</template>
@@ -28,7 +28,6 @@
<script>
import ScrollPane from './ScrollPane'
import path from 'path'
export default {
components: { ScrollPane },
data() {
@@ -45,7 +44,7 @@ export default {
return this.$store.state.tagsView.visitedViews
},
routes() {
return this.$store.state.permission.routers
return this.$store.state.permission.routes
}
},
watch: {
@@ -176,19 +175,20 @@ export default {
const offsetWidth = this.$el.offsetWidth // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary
const left = e.clientX - offsetLeft + 15 // 15: margin right
if (left > maxLeft) {
this.left = maxLeft
} else {
this.left = left
}
this.top = e.clientY
this.visible = true
this.selectedTag = tag
},
closeMenu() {
this.visible = false
},
handleScroll() {
this.closeMenu()
}
}
}

View File

@@ -1,4 +1,4 @@
export { default as AppMain } from './AppMain'
export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar/index.vue'
export { default as TagsView } from './TagsView'
export { default as AppMain } from './AppMain'
export { default as TagsView } from './TagsView/index.vue'

View File

@@ -50,7 +50,65 @@
<el-tooltip placement="top" content="返回顶部">
<back-to-top :visibility-height="100" />
</el-tooltip>
<!-- 详情对话框 -->
<el-dialog :visible.sync="aftersaleDialogVisible" title="售后详情" width="800">
<section ref="print">
<el-form :data="aftersaleDetail" label-position="left">
<el-form-item label="售后id">
<el-tag>{{ aftersaleDetail.id }}</el-tag>
</el-form-item>
<el-form-item label="售后编号">
<el-tag>{{ aftersaleDetail.aftersaleSn }}</el-tag>
</el-form-item>
<el-form-item label="订单号">
<el-tag>{{ aftersaleDetail.orderId }}</el-tag>
</el-form-item>
<el-form-item label="订单金额">
<el-tag>{{ aftersaleDetail.amount }}</el-tag>
</el-form-item>
<el-form-item label="订单状态">
<el-tag v-if="aftersaleDetail.status === 1">已申请,待审核</el-tag>
<el-tag v-if="aftersaleDetail.status === 2">审核通过,待退款</el-tag>
<el-tag v-if="aftersaleDetail.status === 3">退款成功</el-tag>
<el-tag v-if="aftersaleDetail.status === 4">审核不通过,已拒绝</el-tag>
</el-form-item>
<el-form-item label="订单用户id">
<el-tag>{{ aftersaleDetail.userId }}</el-tag>
</el-form-item>
<el-form-item label="售后类型">
<el-tag v-if="aftersaleDetail.type === 0">未收货退款</el-tag>
<el-tag v-if="aftersaleDetail.type === 1">不退货退款</el-tag>
<el-tag v-if="aftersaleDetail.type === 2">退货退款</el-tag>
</el-form-item>
<el-form-item label="退款原因">
<span>{{ aftersaleDetail.reason }}</span>
</el-form-item>
<el-form-item label="申请时间">
<span>{{ aftersaleDetail.addTime }}</span>
</el-form-item>
<el-form-item label="更新时间">
<span>{{ aftersaleDetail.updateTime }}</span>
</el-form-item>
<el-form-item label="处理时间">
<span>{{ aftersaleDetail.handleTime }}</span>
</el-form-item>
<el-form-item label="售后图片">
<el-table :data="aftersaleDetail.pictures" border fit highlight-current-row>
<el-table-column align="center" label="售后图片">
<template slot-scope="scope">
<a :href="scope.row" target="_blank">
<img :src="scope.row" width="40">
</a>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
</section>
<span slot="footer" class="dialog-footer">
<el-button @click="aftersaleDialogVisible = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
@@ -91,7 +149,9 @@ export default {
multipleSelection: [],
contentDetail: '',
contentDialogVisible: false,
downloadLoading: false
downloadLoading: false,
aftersaleDialogVisible:false,
aftersaleDetail:{}
}
},
created() {
@@ -249,7 +309,12 @@ export default {
excel.export_json_to_excel2(tHeader, this.list, filterVal, '售后信息')
this.downloadLoading = false
})
}
},
handleRead(row) {
this.aftersaleDetail = row;
console.log(this.aftersaleDetail);
this.aftersaleDialogVisible = true
},
}
}
</script>

View File

@@ -3,8 +3,8 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.id" clearable class="filter-item" style="width: 200px;" placeholder="请输入品牌商ID"/>
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入品牌商名称"/>
<el-input v-model="listQuery.id" clearable class="filter-item" style="width: 200px;" placeholder="请输入品牌商ID" />
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入品牌商名称" />
<el-button v-permission="['GET /admin/brand/list']" class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button v-permission="['POST /admin/brand/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
@@ -13,9 +13,9 @@
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" label="品牌商ID" prop="id"/>
<el-table-column align="center" label="品牌商ID" prop="id" />
<el-table-column align="center" label="品牌商名称" prop="name"/>
<el-table-column align="center" label="品牌商名称" prop="name" />
<el-table-column align="center" property="picUrl" label="品牌商图片">
<template slot-scope="scope">
@@ -23,9 +23,9 @@
</template>
</el-table-column>
<el-table-column align="center" min-width="400px" label="介绍" prop="desc"/>
<el-table-column align="center" min-width="400px" label="介绍" prop="desc" />
<el-table-column align="center" label="底价" prop="floorPrice"/>
<el-table-column align="center" label="底价" prop="floorPrice" />
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
@@ -41,10 +41,10 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="dataForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="品牌商名称" prop="name">
<el-input v-model="dataForm.name"/>
<el-input v-model="dataForm.name" />
</el-form-item>
<el-form-item label="介绍" prop="simpleDesc">
<el-input v-model="dataForm.desc"/>
<el-input v-model="dataForm.desc" />
</el-form-item>
<el-form-item label="品牌商图片" prop="picUrl">
<el-upload
@@ -53,13 +53,14 @@
:show-file-list="false"
:on-success="uploadPicUrl"
class="avatar-uploader"
accept=".jpg,.jpeg,.png,.gif">
accept=".jpg,.jpeg,.png,.gif"
>
<img v-if="dataForm.picUrl" :src="dataForm.picUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"/>
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
</el-form-item>
<el-form-item label="底价" prop="floorPrice">
<el-input v-model="dataForm.floorPrice"/>
<el-input v-model="dataForm.floorPrice" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -254,8 +255,7 @@ export default {
title: '成功',
message: '删除成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
.catch(response => {
this.$notify.error({

View File

@@ -3,7 +3,7 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.question" clearable class="filter-item" style="width: 200px;" placeholder="请输入问题"/>
<el-input v-model="listQuery.question" clearable class="filter-item" style="width: 200px;" placeholder="请输入问题" />
<el-button v-permission="['GET /admin/issue/list']" class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button v-permission="['POST /admin/issue/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
@@ -11,11 +11,11 @@
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" width="100px" label="问题ID" prop="id" sortable/>
<el-table-column align="center" width="100px" label="问题ID" prop="id" sortable />
<el-table-column align="center" min-width="200px" label="问题内容" prop="question"/>
<el-table-column align="center" min-width="200px" label="问题内容" prop="question" />
<el-table-column align="center" min-width="400px" label="问题回复" prop="answer"/>
<el-table-column align="center" min-width="400px" label="问题回复" prop="answer" />
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
@@ -31,10 +31,10 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="dataForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="问题" prop="question">
<el-input v-model="dataForm.question"/>
<el-input v-model="dataForm.question" />
</el-form-item>
<el-form-item label="回复" prop="answer">
<el-input v-model="dataForm.answer" :rows="8" type="textarea" placeholder="请输入内容"/>
<el-input v-model="dataForm.answer" :rows="8" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -177,8 +177,7 @@ export default {
title: '成功',
message: '删除成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
}).catch(response => {
this.$notify.error({
title: '失败',

View File

@@ -3,8 +3,8 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.keyword" clearable class="filter-item" style="width: 200px;" placeholder="请输入关键字"/>
<el-input v-model="listQuery.url" clearable class="filter-item" style="width: 200px;" placeholder="请输入跳转链接"/>
<el-input v-model="listQuery.keyword" clearable class="filter-item" style="width: 200px;" placeholder="请输入关键字" />
<el-input v-model="listQuery.url" clearable class="filter-item" style="width: 200px;" placeholder="请输入跳转链接" />
<el-button v-permission="['GET /admin/keyword/list']" class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button v-permission="['POST /admin/keyword/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
@@ -13,11 +13,11 @@
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" width="150px" label="关键词ID" prop="id" sortable/>
<el-table-column align="center" width="150px" label="关键词ID" prop="id" sortable />
<el-table-column align="center" min-width="100px" label="关键词" prop="keyword"/>
<el-table-column align="center" min-width="100px" label="关键词" prop="keyword" />
<el-table-column align="center" min-width="300px" label="跳转链接" prop="url"/>
<el-table-column align="center" min-width="300px" label="跳转链接" prop="url" />
<el-table-column align="center" min-width="100px" label="是否推荐" prop="isHot">
<template slot-scope="scope">
@@ -45,21 +45,21 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="dataForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="关键词" prop="keyword">
<el-input v-model="dataForm.keyword"/>
<el-input v-model="dataForm.keyword" />
</el-form-item>
<el-form-item label="跳转链接" prop="url">
<el-input v-model="dataForm.url"/>
<el-input v-model="dataForm.url" />
</el-form-item>
<el-form-item label="是否推荐" prop="isHot">
<el-select v-model="dataForm.isHot" placeholder="请选择">
<el-option :value="true" label="推荐"/>
<el-option :value="false" label="普通"/>
<el-option :value="true" label="推荐" />
<el-option :value="false" label="普通" />
</el-select>
</el-form-item>
<el-form-item label="是否默认" prop="isDefault">
<el-select v-model="dataForm.isDefault" placeholder="请选择">
<el-option :value="true" label="默认"/>
<el-option :value="false" label="非默认"/>
<el-option :value="true" label="默认" />
<el-option :value="false" label="非默认" />
</el-select>
</el-form-item>
</el-form>
@@ -207,8 +207,7 @@ export default {
title: '成功',
message: '删除成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
}).catch(response => {
this.$notify.error({
title: '失败',

View File

@@ -4,6 +4,7 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.userId" clearable class="filter-item" style="width: 160px;" placeholder="请输入用户ID" />
<el-input v-model="listQuery.orderId" clearable class="filter-item" style="width: 160px;" placeholder="请输入订单ID" />
<el-input v-model="listQuery.orderSn" clearable class="filter-item" style="width: 160px;" placeholder="请输入订单编号" />
<el-date-picker v-model="listQuery.timeArray" type="datetimerange" value-format="yyyy-MM-dd HH:mm:ss" class="filter-item" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="pickerOptions" />
<el-select v-model="listQuery.orderStatusArray" multiple style="width: 200px" class="filter-item" placeholder="请选择订单状态">
@@ -190,7 +191,8 @@ export default {
timeArray: [],
orderStatusArray: [],
sort: 'add_time',
order: 'desc'
order: 'desc',
orderId:undefined
},
pickerOptions: {
shortcuts: [{
@@ -256,16 +258,30 @@ export default {
this.listQuery.start = null
this.listQuery.end = null
}
listOrder(this.listQuery).then(response => {
this.list = response.data.data.list
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
if(this.listQuery.orderId){
detailOrder(this.listQuery.orderId).then(response => {
this.list = [];
if(response.data.data.order){
this.list.push(response.data.data.order);
this.total = 1;
this.listLoading = false
}
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
}else{
listOrder(this.listQuery).then(response => {
this.list = response.data.data.list
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
}
},
getChannel() {
listChannel().then(response => {

View File

@@ -112,8 +112,7 @@ export default {
title: '成功',
message: '删除通知成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
.catch(response => {
this.$notify.error({

View File

@@ -3,8 +3,8 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入广告标题"/>
<el-input v-model="listQuery.content" clearable class="filter-item" style="width: 200px;" placeholder="请输入广告内容"/>
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入广告标题" />
<el-input v-model="listQuery.content" clearable class="filter-item" style="width: 200px;" placeholder="请输入广告内容" />
<el-button v-permission="['GET /admin/ad/list']" class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button v-permission="['POST /admin/ad/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
@@ -13,11 +13,11 @@
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" label="广告ID" prop="id" sortable/>
<el-table-column align="center" label="广告ID" prop="id" sortable />
<el-table-column align="center" label="广告标题" prop="name"/>
<el-table-column align="center" label="广告标题" prop="name" />
<el-table-column align="center" label="广告内容" prop="content"/>
<el-table-column align="center" label="广告内容" prop="content" />
<el-table-column align="center" label="广告图片" prop="url">
<template slot-scope="scope">
@@ -25,9 +25,9 @@
</template>
</el-table-column>
<el-table-column align="center" label="广告位置" prop="position"/>
<el-table-column align="center" label="广告位置" prop="position" />
<el-table-column align="center" label="活动链接" prop="link"/>
<el-table-column align="center" label="活动链接" prop="link" />
<el-table-column align="center" label="是否启用" prop="enabled">
<template slot-scope="scope">
@@ -49,10 +49,10 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="dataForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="广告标题" prop="name">
<el-input v-model="dataForm.name"/>
<el-input v-model="dataForm.name" />
</el-form-item>
<el-form-item label="广告内容" prop="content">
<el-input v-model="dataForm.content"/>
<el-input v-model="dataForm.content" />
</el-form-item>
<el-form-item label="广告图片" prop="url">
<el-upload
@@ -62,24 +62,25 @@
:on-success="uploadUrl"
:before-upload="checkFileSize"
class="avatar-uploader"
accept=".jpg,.jpeg,.png,.gif">
accept=".jpg,.jpeg,.png,.gif"
>
<img v-if="dataForm.url" :src="dataForm.url" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"/>
<i v-else class="el-icon-plus avatar-uploader-icon" />
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件且不超过1024kb</div>
</el-upload>
</el-form-item>
<el-form-item label="广告位置" prop="position">
<el-select v-model="dataForm.position" placeholder="请选择">
<el-option :value="1" label="首页"/>
<el-option :value="1" label="首页" />
</el-select>
</el-form-item>
<el-form-item label="活动链接" prop="link">
<el-input v-model="dataForm.link"/>
<el-input v-model="dataForm.link" />
</el-form-item>
<el-form-item label="是否启用" prop="enabled">
<el-select v-model="dataForm.enabled" placeholder="请选择">
<el-option :value="true" label="启用"/>
<el-option :value="false" label="不启用"/>
<el-option :value="true" label="启用" />
<el-option :value="false" label="不启用" />
</el-select>
</el-form-item>
</el-form>
@@ -290,8 +291,7 @@ export default {
title: '成功',
message: '删除成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
.catch(response => {
this.$notify.error({

View File

@@ -3,12 +3,12 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入优惠券标题"/>
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入优惠券标题" />
<el-select v-model="listQuery.type" clearable style="width: 200px" class="filter-item" placeholder="请选择优惠券类型">
<el-option v-for="type in typeOptions" :key="type.value" :label="type.label" :value="type.value"/>
<el-option v-for="type in typeOptions" :key="type.value" :label="type.label" :value="type.value" />
</el-select>
<el-select v-model="listQuery.status" clearable style="width: 200px" class="filter-item" placeholder="请选择优惠券状态">
<el-option v-for="type in statusOptions" :key="type.value" :label="type.label" :value="type.value"/>
<el-option v-for="type in statusOptions" :key="type.value" :label="type.label" :value="type.value" />
</el-select>
<el-button v-permission="['GET /admin/coupon/list']" class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button v-permission="['POST /admin/coupon/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
@@ -18,13 +18,13 @@
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" label="优惠券ID" prop="id" sortable/>
<el-table-column align="center" label="优惠券ID" prop="id" sortable />
<el-table-column align="center" label="优惠券名称" prop="name"/>
<el-table-column align="center" label="优惠券名称" prop="name" />
<el-table-column align="center" label="介绍" prop="desc"/>
<el-table-column align="center" label="介绍" prop="desc" />
<el-table-column align="center" label="标签" prop="tag"/>
<el-table-column align="center" label="标签" prop="tag" />
<el-table-column align="center" label="最低消费" prop="min">
<template slot-scope="scope">{{ scope.row.min }}元可用</template>
@@ -69,13 +69,13 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="dataForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="优惠券名称" prop="name">
<el-input v-model="dataForm.name"/>
<el-input v-model="dataForm.name" />
</el-form-item>
<el-form-item label="介绍" prop="desc">
<el-input v-model="dataForm.desc"/>
<el-input v-model="dataForm.desc" />
</el-form-item>
<el-form-item label="标签" prop="tag">
<el-input v-model="dataForm.tag"/>
<el-input v-model="dataForm.tag" />
</el-form-item>
<el-form-item label="最低消费" prop="min">
<el-input v-model="dataForm.min">
@@ -98,7 +98,8 @@
v-for="type in typeOptions"
:key="type.value"
:label="type.label"
:value="type.value"/>
:value="type.value"
/>
</el-select>
</el-form-item>
<el-form-item label="优惠券数量" prop="total">
@@ -119,11 +120,11 @@
</el-form-item>
<el-form-item v-show="dataForm.timeType === 1">
<el-col :span="11">
<el-date-picker v-model="dataForm.startTime" type="datetime" placeholder="选择日期" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%;"/>
<el-date-picker v-model="dataForm.startTime" type="datetime" placeholder="选择日期" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%;" />
</el-col>
<el-col :span="2" class="line"></el-col>
<el-col :span="11">
<el-date-picker v-model="dataForm.endTime" type="datetime" placeholder="选择日期" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%;"/>
<el-date-picker v-model="dataForm.endTime" type="datetime" placeholder="选择日期" value-format="yyyy-MM-dd HH:mm:ss" style="width: 100%;" />
</el-col>
</el-form-item>
<el-form-item label="商品限制范围">
@@ -134,10 +135,76 @@
</el-radio-group>
</el-form-item>
<el-form-item v-show="dataForm.goodsType === 1">
目前不支持
<el-cascader
v-model="selectGoodsCategory"
clearable
placeholder="请选择分类名称"
:options="goodsCategoryOptions"
/>
<el-button @click="handleAddGoodsCategory()">添加</el-button>
<el-table
ref="goodsCateRelationTable"
:data="couponCategoryList"
style="width: 100%;margin-top: 20px"
border
>
<el-table-column label="分类名称" align="center">
<template slot-scope="scope">{{ scope.row.parentCategoryName }}>{{ scope.row.goodsCategoryName }}</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
@click="handleDeleteGoodsCategory(scope.$index, scope.row)"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item v-show="dataForm.goodsType === 2">
目前不支持
<el-select
v-model="selectGoods"
filterable
remote
reserve-keyword
placeholder="商品名称/商品货号"
>
<el-option
v-for="item in goodsOptions"
:key="item.goodsId"
:label="item.goodsName"
:value="item.goodsId"
>
<span style="float: left">{{ item.goodsName }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">NO.{{ item.goodsSn }}</span>
</el-option>
</el-select>
<el-button @click="handleAddGoods()">添加</el-button>
<el-table
ref="goodsRelationTable"
:data="couponGoodsList"
style="width: 100%;margin-top: 20px"
border
>
<el-table-column label="商品名称" align="center">
<template slot-scope="scope">{{ scope.row.goodsName }}</template>
</el-table-column>
<el-table-column label="商品编号" align="center" width="80">
<template slot-scope="scope">{{ scope.row.goodsSn }}</template>
</el-table-column>
<el-table-column label="操作" align="center" width="60">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
@click="handleDeleteGoods(scope.$index, scope.row)"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -178,6 +245,8 @@
<script>
import { listCoupon, createCoupon, updateCoupon, deleteCoupon } from '@/api/coupon'
import { listCategory } from '@/api/category.js'
import { listGoods } from '@/api/goods.js'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
const defaultTypeOptions = [
@@ -286,11 +355,19 @@ export default {
{ required: true, message: '优惠券标题不能为空', trigger: 'blur' }
]
},
downloadLoading: false
downloadLoading: false,
selectGoods: null,
goodsOptions: [],
selectGoodsCategory: null,
goodsCategoryOptions: [],
couponGoodsList: [],
couponCategoryList: []
}
},
created() {
this.getList()
this.getCategoryList()
this.getGoodsList()
},
methods: {
getList() {
@@ -330,6 +407,8 @@ export default {
startTime: null,
endTime: null
}
this.couponCategoryList = []
this.couponGoodsList = []
},
handleCreate() {
this.resetForm()
@@ -342,6 +421,12 @@ export default {
createData() {
this.$refs['dataForm'].validate(valid => {
if (valid) {
if (this.dataForm.goodsType === 1) {
this.dataForm.goodsValue = this.couponCategoryList.map(item => (item.goodsCategoryId))
}
if (this.dataForm.goodsType === 2) {
this.dataForm.goodsValue = this.couponGoodsList.map(item => (item.goodsId))
}
createCoupon(this.dataForm)
.then(response => {
this.list.unshift(response.data.data)
@@ -408,8 +493,7 @@ export default {
title: '成功',
message: '删除优惠券成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
.catch(response => {
this.$notify.error({
@@ -447,6 +531,84 @@ export default {
excel.export_json_to_excel2(tHeader, this.list, filterVal, '优惠券信息')
this.downloadLoading = false
})
},
getGoodsList() {
listGoods({ limit: 0 }).then(response => {
const goodsList = response.data.data.list
this.goodsOptions = []
for (let i = 0; i < goodsList.length; i++) {
const item = goodsList[i]
this.goodsOptions.push({ goodsId: item.id, goodsName: item.name, goodsSn: item.goodsSn })
}
}).catch(() => {
this.goodsOptions = []
})
},
handleAddGoods() {
if (this.selectGoods === null) {
this.$message({
message: '请先选择商品',
type: 'warning'
})
return
}
this.couponGoodsList.push(this.getGoodsById(this.selectGoods))
this.selectGoods = null
},
handleDeleteGoods(index, row) {
this.couponGoodsList.splice(index, 1)
},
handleAddGoodsCategory() {
if (this.selectGoodsCategory === null || this.selectGoodsCategory.length === 0) {
this.$message({
message: '请先选择商品分类',
type: 'warning'
})
return
}
this.couponCategoryList.push(this.getGoodsCategoryByIds(this.selectGoodsCategory))
this.selectGoodsCategory = []
},
handleDeleteGoodsCategory(index, row) {
this.couponCategoryList.splice(index, 1)
},
getGoodsById(id) {
for (let i = 0; i < this.goodsOptions.length; i++) {
if (id === this.goodsOptions[i].goodsId) {
return this.goodsOptions[i]
}
}
return null
},
getCategoryList() {
listCategory().then(response => {
const list = response.data.data.list
this.goodsCategoryOptions = []
for (let i = 0; i < list.length; i++) {
const 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.goodsCategoryOptions.push({ label: list[i].name, value: list[i].id, children: children })
}
})
},
getGoodsCategoryByIds(ids) {
let name
let parentName
for (let i = 0; i < this.goodsCategoryOptions.length; i++) {
if (this.goodsCategoryOptions[i].value === ids[0]) {
parentName = this.goodsCategoryOptions[i].label
for (let j = 0; j < this.goodsCategoryOptions[i].children.length; j++) {
if (this.goodsCategoryOptions[i].children[j].value === ids[1]) {
name = this.goodsCategoryOptions[i].children[j].label
}
}
}
}
return { goodsCategoryId: ids[1], goodsCategoryName: name, parentCategoryName: parentName }
}
}
}

View File

@@ -237,8 +237,7 @@ export default {
title: '成功',
message: '删除团购规则成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
}).catch(response => {
this.$notify.error({
title: '失败',

View File

@@ -14,7 +14,7 @@
<div class="operator-container">
<el-button v-permission="['POST /admin/topic/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
<el-button v-permission="['GET /admin/topic/batch-delete']" class="filter-item" type="danger" icon="el-icon-delete" @click="handleBatchDelete">批量删除</el-button>
<el-button v-permission="['POST /admin/topic/batch-delete']" class="filter-item" type="danger" icon="el-icon-delete" @click="handleBatchDelete">批量删除</el-button>
</div>
<!-- 查询结果 -->
@@ -163,8 +163,7 @@ export default {
title: '成功',
message: '删除专题成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
.catch(response => {
this.$notify.error({

View File

@@ -3,7 +3,7 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.username" clearable class="filter-item" style="width: 200px;" placeholder="请输入管理员名称"/>
<el-input v-model="listQuery.username" clearable class="filter-item" style="width: 200px;" placeholder="请输入管理员名称" />
<el-button v-permission="['GET /admin/admin/list']" class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button v-permission="['POST /admin/admin/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
@@ -11,9 +11,9 @@
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" label="管理员ID" prop="id" sortable/>
<el-table-column align="center" label="管理员ID" prop="id" sortable />
<el-table-column align="center" label="管理员名称" prop="username"/>
<el-table-column align="center" label="管理员名称" prop="username" />
<el-table-column align="center" label="管理员头像" prop="avatar">
<template slot-scope="scope">
@@ -41,10 +41,10 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="dataForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="管理员名称" prop="username">
<el-input v-model="dataForm.username"/>
<el-input v-model="dataForm.username" />
</el-form-item>
<el-form-item label="管理员密码" prop="password">
<el-input v-model="dataForm.password" type="password" auto-complete="off"/>
<el-input v-model="dataForm.password" type="password" auto-complete="off" />
</el-form-item>
<el-form-item label="管理员头像" prop="avatar">
<el-upload
@@ -53,9 +53,10 @@
:show-file-list="false"
:on-success="uploadAvatar"
class="avatar-uploader"
accept=".jpg,.jpeg,.png,.gif">
accept=".jpg,.jpeg,.png,.gif"
>
<img v-if="dataForm.avatar" :src="dataForm.avatar" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"/>
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
</el-form-item>
<el-form-item label="管理员角色" prop="roleIds">
@@ -64,7 +65,8 @@
v-for="item in roleOptions"
:key="item.value"
:label="item.label"
:value="item.value"/>
:value="item.value"
/>
</el-select>
</el-form-item>
</el-form>
@@ -275,8 +277,7 @@ export default {
title: '成功',
message: '删除管理员成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
.catch(response => {
this.$notify.error({

View File

@@ -237,8 +237,7 @@ export default {
title: '成功',
message: '删除通知成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
.catch(response => {
this.$notify.error({

View File

@@ -3,8 +3,8 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.key" clearable class="filter-item" style="width: 200px;" placeholder="请输入对象KEY"/>
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入对象名称"/>
<el-input v-model="listQuery.key" clearable class="filter-item" style="width: 200px;" placeholder="请输入对象KEY" />
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入对象名称" />
<el-button v-permission="['GET /admin/storage/list']" class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button v-permission="['POST /admin/storage/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
@@ -13,13 +13,13 @@
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" label="对象KEY" prop="key"/>
<el-table-column align="center" label="对象KEY" prop="key" />
<el-table-column align="center" label="对象名称" prop="name"/>
<el-table-column align="center" label="对象名称" prop="name" />
<el-table-column align="center" label="对象类型" prop="type"/>
<el-table-column align="center" label="对象类型" prop="type" />
<el-table-column align="center" label="对象大小" prop="size"/>
<el-table-column align="center" label="对象大小" prop="size" />
<el-table-column align="center" property="url" label="图片">
<template slot-scope="scope">
@@ -27,7 +27,7 @@
</template>
</el-table-column>
<el-table-column align="center" label="图片链接" prop="url"/>
<el-table-column align="center" label="图片链接" prop="url" />
<el-table-column align="center" label="操作" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
@@ -50,7 +50,7 @@
<el-dialog :visible.sync="updateDialogVisible" title="修改对象名称">
<el-form ref="dataForm" :rules="rules" :model="dataForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="对象名称" prop="name">
<el-input v-model="dataForm.name"/>
<el-input v-model="dataForm.name" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -177,8 +177,7 @@ export default {
title: '成功',
message: '删除成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
}).catch(response => {
this.$notify.error({
title: '失败',

View File

@@ -3,16 +3,16 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入角色名称"/>
<el-input v-model="listQuery.name" clearable class="filter-item" style="width: 200px;" placeholder="请输入角色名称" />
<el-button v-permission="['GET /admin/role/list']" class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button v-permission="['POST /admin/role/create']" class="filter-item" type="primary" icon="el-icon-edit" @click="handleCreate">添加</el-button>
</div>
<!-- 查询结果 -->
<el-table v-loading="listLoading" :data="list" element-loading-text="正在查询中。。。" border fit highlight-current-row>
<el-table-column align="center" label="角色名称" prop="name" sortable/>
<el-table-column align="center" label="角色名称" prop="name" sortable />
<el-table-column align="center" label="说明" prop="desc"/>
<el-table-column align="center" label="说明" prop="desc" />
<el-table-column align="center" label="操作" class-name="small-padding fixed-width">
<template slot-scope="scope">
@@ -29,10 +29,10 @@
<el-dialog :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<el-form ref="dataForm" :rules="rules" :model="dataForm" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="角色名称" prop="name">
<el-input v-model="dataForm.name"/>
<el-input v-model="dataForm.name" />
</el-form-item>
<el-form-item label="说明" prop="desc">
<el-input v-model="dataForm.desc"/>
<el-input v-model="dataForm.desc" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -50,7 +50,8 @@
:default-checked-keys="assignedPermissions"
show-checkbox
node-key="id"
highlight-current>
highlight-current
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<span>{{ data.label }}</span>
<el-tag v-if="data.api" type="success" size="mini">{{ data.api }}</el-tag>
@@ -208,8 +209,7 @@ export default {
title: '成功',
message: '删除管理员成功'
})
const index = this.list.indexOf(row)
this.list.splice(index, 1)
this.getList()
})
.catch(response => {
this.$notify.error({

View File

@@ -4,6 +4,7 @@
<!-- 查询和其他操作 -->
<div class="filter-container">
<el-input v-model="listQuery.username" clearable class="filter-item" style="width: 200px;" placeholder="请输入用户名"/>
<el-input v-model="listQuery.userId" clearable class="filter-item" style="width: 200px;" placeholder="请输入用户Id"/>
<el-input v-model="listQuery.mobile" clearable class="filter-item" style="width: 200px;" placeholder="请输入手机号"/>
<el-button class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">查找</el-button>
<el-button :loading="downloadLoading" class="filter-item" type="primary" icon="el-icon-download" @click="handleDownload">导出</el-button>
@@ -36,16 +37,49 @@
<el-tag>{{ statusDic[scope.row.status] }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="操作" width="250" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="handleDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="getList" />
<!-- 用户编辑对话框 -->
<el-dialog :visible.sync="userDialogVisible" title="用户编辑">
<el-form ref="userDetail" :model="userDetail" status-icon label-position="left" label-width="100px" style="width: 400px; margin-left:50px;">
<el-form-item label="用户名" prop="username">
<el-input v-model="userDetail.username" :disabled="true" />
</el-form-item>
<el-form-item label="用户昵称" prop="nickname">
<el-input v-model="userDetail.nickname" />
</el-form-item>
<el-form-item label="用户密码" prop="mobile">
<el-input v-model="userDetail.password" />
</el-form-item>
<el-form-item label="用户手机" prop="mobile">
<el-input v-model="userDetail.mobile" />
</el-form-item>
<el-form-item label="用户性别" prop="gender">
<el-select v-model="userDetail.gender" placeholder="请选择"><el-option v-for="(item, index) in genderDic" :key="index" :label="item" :value="index" /></el-select>
</el-form-item>
<el-form-item label="用户等级" prop="userLevel">
<el-select v-model="userDetail.userLevel" placeholder="请选择"><el-option v-for="(item, index) in levelDic" :key="index" :label="item" :value="index" /></el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="userDetail.status" placeholder="请选择"><el-option v-for="(item, index) in statusDic" :key="index" :label="item" :value="index" /></el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="userDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleUserUpdate">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { fetchList } from '@/api/user'
import { fetchList ,userDetail ,updateUser } from '@/api/user'
import Pagination from '@/components/Pagination' // Secondary package based on el-pagination
export default {
@@ -61,13 +95,17 @@ export default {
limit: 20,
username: undefined,
mobile: undefined,
userId: undefined,
sort: 'add_time',
order: 'desc'
},
downloadLoading: false,
genderDic: ['未知', '男', '女'],
levelDic: ['普通用户', 'VIP用户', '高级VIP用户'],
statusDic: ['可用', '禁用', '注销']
statusDic: ['可用', '禁用', '注销'],
userDialogVisible: false,
userDetail:{
}
}
},
created() {
@@ -76,15 +114,34 @@ export default {
methods: {
getList() {
this.listLoading = true
fetchList(this.listQuery).then(response => {
this.list = response.data.data.list
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
if(this.listQuery.userId){
userDetail(this.listQuery.userId).then(response => {
this.list = [];
if(response.data.data){
this.list.push(response.data.data)
this.total = 1
this.listLoading = false
}else{
this.list = []
this.total = 0
this.listLoading = false
}
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
}else{
fetchList(this.listQuery).then(response => {
this.list = response.data.data.list
this.total = response.data.data.total
this.listLoading = false
}).catch(() => {
this.list = []
this.total = 0
this.listLoading = false
})
}
},
handleFilter() {
this.listQuery.page = 1
@@ -98,6 +155,26 @@ export default {
excel.export_json_to_excel2(tHeader, this.list, filterVal, '用户信息')
this.downloadLoading = false
})
},
handleDetail(row) {
this.userDetail = row
this.userDialogVisible = true
},
handleUserUpdate(){
updateUser(this.userDetail)
.then((response) => {
this.userDialogVisible = false
this.$notify.success({
title: '成功',
message: '更新用户成功'
})
})
.catch(response => {
this.$notify.error({
title: '失败',
message: response.data.errmsg
})
})
}
}
}

View File

@@ -29,7 +29,12 @@ module.exports = {
lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false,
devServer: {
port: port
port: port,
open: true,
overlay: {
warnings: false,
errors: true
}
},
configureWebpack: {
// provide the app's title in webpack's name field, so that
@@ -42,8 +47,11 @@ module.exports = {
}
},
chainWebpack(config) {
config.plugins.delete('preload') // TODO: need test
config.plugins.delete('prefetch') // TODO: need test
// it can improve the speed of the first screen, it is recommended to turn on preload
// config.plugins.delete('preload')
// when there are many pages, it will cause too many meaningless requests
config.plugins.delete('prefetch') //
// set svg-sprite-loader
config.module
@@ -73,12 +81,6 @@ module.exports = {
})
.end()
config
// https://webpack.js.org/configuration/devtool/#development
.when(process.env.NODE_ENV === 'development',
config => config.devtool('cheap-source-map')
)
config
.when(process.env.NODE_ENV !== 'development',
config => {

View File

@@ -42,8 +42,8 @@
</commentGenerator>
<!--数据库连接信息-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/litemall?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC&amp;verifyServerCertificate=false&amp;useSSL=false"
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/litemall?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC&amp;verifyServerCertificate=false&amp;useSSL=false&amp;nullCatalogMeansCurrent=true"
userId="litemall"
password="litemall123456"/>

View File

@@ -57,7 +57,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<version>8.0.16</version>
</dependency>
<dependency>
<groupId>com.itfsw</groupId>

View File

@@ -110,7 +110,7 @@ CREATE TABLE `litemall_aftersale` (
`handle_time` datetime DEFAULT NULL COMMENT '管理员操作时间',
`add_time` datetime DEFAULT NULL COMMENT '添加时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`deleted` tinyint(1) DEFAULT '0' COMMENT '售后编号',
`deleted` tinyint(1) DEFAULT '0' COMMENT '逻辑删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='售后表';
/*!40101 SET character_set_client = @saved_cs_client */;

View File

@@ -1,5 +1,6 @@
package org.linlinjava.litemall.db.service;
import org.linlinjava.litemall.db.domain.LitemallCart;
import org.linlinjava.litemall.db.domain.LitemallCoupon;
import org.linlinjava.litemall.db.domain.LitemallCouponUser;
import org.linlinjava.litemall.db.util.CouponConstant;
@@ -8,6 +9,7 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
@Service
public class CouponVerifyService {
@@ -16,6 +18,8 @@ public class CouponVerifyService {
private LitemallCouponUserService couponUserService;
@Autowired
private LitemallCouponService couponService;
@Autowired
private LitemallGoodsService goodsService;
/**
* 检测优惠券是否适合
@@ -25,9 +29,9 @@ public class CouponVerifyService {
* @param checkedGoodsPrice
* @return
*/
public LitemallCoupon checkCoupon(Integer userId, Integer couponId, Integer userCouponId, BigDecimal checkedGoodsPrice) {
public LitemallCoupon checkCoupon(Integer userId, Integer couponId, Integer userCouponId, BigDecimal checkedGoodsPrice, List<LitemallCart> cartList) {
LitemallCoupon coupon = couponService.findById(couponId);
if (coupon == null) {
if (coupon == null || coupon.getDeleted()) {
return null;
}
@@ -62,10 +66,38 @@ public class CouponVerifyService {
}
// 检测商品是否符合
// TODO 目前仅支持全平台商品,所以不需要检测
Map<Integer, List<LitemallCart>> cartMap = new HashMap<>();
//可使用优惠券的商品或分类
List<Integer> goodsValueList = new ArrayList<>(Arrays.asList(coupon.getGoodsValue()));
Short goodType = coupon.getGoodsType();
if (!goodType.equals(CouponConstant.GOODS_TYPE_ALL)) {
return null;
if (goodType.equals(CouponConstant.GOODS_TYPE_CATEGORY) ||
goodType.equals((CouponConstant.GOODS_TYPE_ARRAY))) {
for (LitemallCart cart : cartList) {
Integer key = goodType.equals(CouponConstant.GOODS_TYPE_ARRAY) ? cart.getGoodsId() :
goodsService.findById(cart.getGoodsId()).getCategoryId();
List<LitemallCart> carts = cartMap.get(key);
if (carts == null) {
carts = new LinkedList<>();
}
carts.add(cart);
cartMap.put(key, carts);
}
//购物车中可以使用优惠券的商品或分类
goodsValueList.retainAll(cartMap.keySet());
//可使用优惠券的商品的总价格
BigDecimal total = new BigDecimal(0);
for (Integer goodsId : goodsValueList) {
List<LitemallCart> carts = cartMap.get(goodsId);
for (LitemallCart cart : carts) {
total = total.add(cart.getPrice().multiply(new BigDecimal(cart.getNumber())));
}
}
//是否达到优惠券满减金额
if (total.compareTo(coupon.getMin()) == -1) {
return null;
}
}
// 检测订单状态

View File

@@ -7,8 +7,8 @@ pagehelper:
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/litemall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&verifyServerCertificate=false&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: litemall
password: litemall123456
initial-size: 10

View File

@@ -4191,9 +4191,9 @@
"dev": true
},
"elliptic": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"dev": true,
"requires": {
"bn.js": "^4.4.0",
@@ -6748,9 +6748,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
},
"lodash.defaultsdeep": {
"version": "4.6.1",

View File

@@ -18,7 +18,7 @@
"axios": ">=0.18.1",
"dayjs": "^1.7.7",
"js-cookie": "2.2.0",
"lodash": "^4.17.11",
"lodash": "^4.17.19",
"vant": "^2.0.6",
"vue": "^2.5.17",
"vue-router": "^3.0.1",

View File

@@ -1,6 +1,5 @@
<template>
<div>
<van-nav-bar title="收货地址" left-text="返回" left-arrow @click-left="goback"/>
<van-address-list v-model="chosenAddressId" :list="addressList" @add="onAdd" @edit="onEdit" @select="onSelect"/>
</div>
</template>

View File

@@ -290,8 +290,13 @@ public class WxOrderService {
}
// 2不允许参加自己开团的团购
LitemallGroupon groupon = grouponService.queryById(userId, grouponLinkId);
if(groupon.getCreatorUserId().equals(userId)){
return ResponseUtil.fail(GROUPON_JOIN, "团购活动已经参加!");
// if(groupon.getCreatorUserId().equals(userId)){
// return ResponseUtil.fail(GROUPON_JOIN, "团购活动已经参加!");
// }
if(groupon!=null) {
if(groupon.getCreatorUserId().equals(userId)){
return ResponseUtil.fail(GROUPON_JOIN, "团购活动已经参加!");
}
}
}
}
@@ -340,7 +345,7 @@ public class WxOrderService {
BigDecimal couponPrice = new BigDecimal(0);
// 如果couponId=0则没有优惠券couponId=-1则不使用优惠券
if (couponId != 0 && couponId != -1) {
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, userCouponId, checkedGoodsPrice);
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, userCouponId, checkedGoodsPrice, checkedGoodsList);
if (coupon == null) {
return ResponseUtil.badArgumentValue();
}
@@ -485,7 +490,7 @@ public class WxOrderService {
* 1. 检测当前订单是否能够取消;
* 2. 设置订单取消状态;
* 3. 商品货品库存恢复;
* 4. TODO 优惠券;
* 4. 返还优惠券;
*
* @param userId 用户ID
* @param body 订单信息,{ orderIdxxx }

View File

@@ -79,7 +79,7 @@ public class WxAddressController extends GetRegionService {
if (StringUtils.isEmpty(mobile)) {
return ResponseUtil.badArgument();
}
if (!RegexUtil.isMobileExact(mobile)) {
if (!RegexUtil.isMobileSimple(mobile)) {
return ResponseUtil.badArgument();
}

View File

@@ -189,7 +189,7 @@ public class WxAuthController {
if (StringUtils.isEmpty(phoneNumber)) {
return ResponseUtil.badArgument();
}
if (!RegexUtil.isMobileExact(phoneNumber)) {
if (!RegexUtil.isMobileSimple(phoneNumber)) {
return ResponseUtil.badArgumentValue();
}
@@ -256,7 +256,7 @@ public class WxAuthController {
if (userList.size() > 0) {
return ResponseUtil.fail(AUTH_MOBILE_REGISTERED, "手机号已注册");
}
if (!RegexUtil.isMobileExact(mobile)) {
if (!RegexUtil.isMobileSimple(mobile)) {
return ResponseUtil.fail(AUTH_INVALID_MOBILE, "手机号格式不正确");
}
//判断验证码是否正确
@@ -343,7 +343,7 @@ public class WxAuthController {
if (StringUtils.isEmpty(phoneNumber)) {
return ResponseUtil.badArgument();
}
if (!RegexUtil.isMobileExact(phoneNumber)) {
if (!RegexUtil.isMobileSimple(phoneNumber)) {
return ResponseUtil.badArgumentValue();
}
if (StringUtils.isEmpty(captchaType)) {

View File

@@ -469,7 +469,7 @@ public class WxCartController {
int tmpCouponLength = 0;
List<LitemallCouponUser> couponUserList = couponUserService.queryAll(userId);
for(LitemallCouponUser couponUser : couponUserList){
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponUser.getCouponId(), couponUser.getId(), checkedGoodsPrice);
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponUser.getCouponId(), couponUser.getId(), checkedGoodsPrice, checkedGoodsList);
if(coupon == null){
continue;
}
@@ -498,7 +498,7 @@ public class WxCartController {
userCouponId = tmpUserCouponId;
}
else {
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, userCouponId, checkedGoodsPrice);
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, couponId, userCouponId, checkedGoodsPrice, checkedGoodsList);
// 用户选择的优惠券有问题,则选择合适优惠券,否则使用用户选择的优惠券
if(coupon == null){
couponPrice = tmpCouponPrice;

View File

@@ -155,12 +155,11 @@ public class WxCouponController {
checkedGoodsPrice = checkedGoodsPrice.add(cart.getPrice().multiply(new BigDecimal(cart.getNumber())));
}
}
// 计算优惠券可用情况
List<LitemallCouponUser> couponUserList = couponUserService.queryAll(userId);
List<CouponVo> couponVoList = change(couponUserList);
for (CouponVo cv : couponVoList) {
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, cv.getCid(), cv.getId(), checkedGoodsPrice);
LitemallCoupon coupon = couponVerifyService.checkCoupon(userId, cv.getCid(), cv.getId(), checkedGoodsPrice, checkedGoodsList);
cv.setAvailable(coupon != null);
}

View File

@@ -55,7 +55,7 @@ public class WxFeedbackController {
if (StringUtils.isEmpty(mobile)) {
return ResponseUtil.badArgument();
}
if (!RegexUtil.isMobileExact(mobile)) {
if (!RegexUtil.isMobileSimple(mobile)) {
return ResponseUtil.badArgument();
}
return null;

View File

@@ -1,7 +1,6 @@
package org.linlinjava.litemall.wx.web;
import com.github.pagehelper.PageInfo;
import com.mysql.jdbc.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.core.system.SystemConfig;
@@ -12,6 +11,7 @@ import org.linlinjava.litemall.db.domain.*;
import org.linlinjava.litemall.db.service.*;
import org.linlinjava.litemall.wx.annotation.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -259,7 +259,7 @@ public class WxGoodsController {
@Order @RequestParam(defaultValue = "desc") String order) {
//添加到搜索历史
if (userId != null && !StringUtils.isNullOrEmpty(keyword)) {
if (userId != null && !StringUtils.isEmpty(keyword)) {
LitemallSearchHistory searchHistoryVo = new LitemallSearchHistory();
searchHistoryVo.setKeyword(keyword);
searchHistoryVo.setUserId(userId);

View File

@@ -3,6 +3,7 @@ package org.linlinjava.litemall.wx.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -19,7 +20,7 @@ public class WxIndexController {
*
* @return 测试数据
*/
@RequestMapping("/index")
@GetMapping("/index")
public Object index() {
return ResponseUtil.ok("hello world, this is wx service");
}

View File

@@ -28,7 +28,7 @@ public class WxIssueController {
/**
* 帮助中心
*/
@RequestMapping("/list")
@GetMapping("/list")
public Object list(String question,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer size,

View File

@@ -42,15 +42,6 @@ Page({
return false;
}
if (!check.isValidPhone(this.data.mobile)) {
wx.showModal({
title: '错误信息',
content: '手机号输入不正确',
showCancel: false
});
return false;
}
wx.request({
url: api.AuthRegisterCaptcha,
data: {
@@ -145,15 +136,6 @@ Page({
return false;
}
if (!check.isValidPhone(this.data.mobile)) {
wx.showModal({
title: '错误信息',
content: '手机号输入不正确',
showCancel: false
});
return false;
}
wx.login({
success: function(res) {
if (!res.code) {

View File

@@ -70,15 +70,6 @@ Page({
return false;
}
if (!check.isValidPhone(this.data.mobile)) {
wx.showModal({
title: '错误信息',
content: '手机号输入不正确',
showCancel: false
});
return false;
}
if (this.data.password.length < 3) {
wx.showModal({
title: '错误信息',

View File

@@ -177,7 +177,7 @@
</view>
</view>
<view class="good-grid" wx:for="{{floorGoods}}" wx:key="id">
<view class="h">
<view class="h" wx:if="{{item.goodsList.length>0}}">
<text>{{item.name}}</text>
</view>
<view class="b">
@@ -191,7 +191,7 @@
</view>
</block>
</view>
<navigator url="/pages/category/category?id={{item.id}}" class="t">
<navigator url="/pages/category/category?id={{item.id}}" class="t" wx:if="{{item.goodsList.length>0}}">
<view class="txt">{{'更多'+item.name+'好物 >'}}</view>
</navigator>
</view>

View File

@@ -322,11 +322,6 @@ Page({
return false;
}
if (!check.isValidPhone(address.tel)) {
util.showErrorToast('手机号不正确');
return false;
}
let that = this;
util.request(api.AddressSave, {
id: address.id,

View File

@@ -26,6 +26,7 @@ page {
margin-bottom: 30rpx;
margin-left: 30rpx;
margin-right: 30rpx;
line-height: 80rpx;
}
.item {

View File

@@ -117,14 +117,6 @@ Page({
return false;
}
if (!check.isValidPhone(this.data.mobile)) {
this.setData({
mobile: ''
});
util.showErrorToast('请输入手机号码');
return false;
}
wx.showLoading({
title: '提交中...',
mask: true,

View File

@@ -86,7 +86,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
<version>8.0.16</version>
</dependency>
<dependency>

View File

@@ -322,11 +322,6 @@ Page({
return false;
}
if (!check.isValidPhone(address.tel)) {
util.showErrorToast('手机号不正确');
return false;
}
let that = this;
util.request(api.AddressSave, {
id: address.id,