增加权限审核,限制显示内容,细节优化
This commit is contained in:
@@ -81,7 +81,7 @@ const loading = ref(false)
|
|||||||
const getCaptcha = async () => {
|
const getCaptcha = async () => {
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const response = await api.auth.captcha('/captcha/generate')
|
const response = await api.auth.captcha()
|
||||||
|
|
||||||
if (response.data.success) {
|
if (response.data.success) {
|
||||||
captchaImage.value = response.data.data.image
|
captchaImage.value = response.data.data.image
|
||||||
|
|||||||
@@ -20,16 +20,16 @@
|
|||||||
<template #title>仪表盘</template>
|
<template #title>仪表盘</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
|
||||||
<el-menu-item v-if="userStore.isAdmin" index="/products">
|
<el-menu-item v-if="userStore.isSupplier" index="/products">
|
||||||
<el-icon><Goods /></el-icon>
|
<el-icon><Goods /></el-icon>
|
||||||
<template #title>商品管理</template>
|
<template #title>商品管理</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
|
|
||||||
<el-menu-item v-if="userStore.isAdmin" index="/orders">
|
<el-menu-item v-if="userStore.isSupplier" index="/orders">
|
||||||
<el-icon><List /></el-icon>
|
<el-icon><List /></el-icon>
|
||||||
<template #title>订单管理</template>
|
<template #title>订单管理</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
<el-menu-item v-if="userStore.isAdmin" index="/withdrawals">
|
<el-menu-item v-if="userStore.isSupplier" index="/withdrawals">
|
||||||
<el-icon><Money /></el-icon>
|
<el-icon><Money /></el-icon>
|
||||||
<template #title>提现审批</template>
|
<template #title>提现审批</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
@@ -199,6 +199,7 @@ import {
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
import api from '@/utils/api'
|
||||||
|
|
||||||
// 组件挂载时不再自动验证token,避免登录后立即触发401错误
|
// 组件挂载时不再自动验证token,避免登录后立即触发401错误
|
||||||
// token验证交给具体的API调用时处理
|
// token验证交给具体的API调用时处理
|
||||||
@@ -228,8 +229,8 @@ const passwordRules = {
|
|||||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||||
{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
|
{ min: 6, message: '密码长度不能少于6位', trigger: 'blur' },
|
||||||
{
|
{
|
||||||
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{6,}$/,
|
pattern: /^(?=.*[a-zA-Z])(?=.*\d)/,
|
||||||
message: '密码必须包含大小写字母和数字',
|
message: '密码必须包含字母和数字',
|
||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -320,12 +321,13 @@ const handleChangePassword = async () => {
|
|||||||
try {
|
try {
|
||||||
await passwordFormRef.value.validate()
|
await passwordFormRef.value.validate()
|
||||||
|
|
||||||
const result = await userStore.changePassword({
|
const result = await api.users.changePassword({
|
||||||
currentPassword: passwordForm.value.currentPassword,
|
id: userStore.user.id,
|
||||||
|
oldPassword: passwordForm.value.currentPassword,
|
||||||
newPassword: passwordForm.value.newPassword
|
newPassword: passwordForm.value.newPassword
|
||||||
})
|
})
|
||||||
|
if (result.data.success) {
|
||||||
if (result.success) {
|
ElMessage.success('密码修改成功')
|
||||||
passwordDialogVisible.value = false
|
passwordDialogVisible.value = false
|
||||||
resetPasswordForm()
|
resetPasswordForm()
|
||||||
|
|
||||||
|
|||||||
@@ -160,11 +160,22 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查管理员权限
|
// 检查管理员权限
|
||||||
if (to.meta.requiresAdmin && !userStore.isAdmin) {
|
if (!userStore.isSupplier || userStore.isDelete) {
|
||||||
ElMessage.error('您没有权限访问此页面')
|
ElMessage.error('您没有权限访问此页面')
|
||||||
next('/dashboard')
|
next('/dashboard')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查审核状态
|
||||||
|
if (userStore.audit_status === 'pending') {
|
||||||
|
ElMessage.warning('您的账号审核中,请稍后登录')
|
||||||
|
next('/dashboard')
|
||||||
|
return
|
||||||
|
} else if (userStore.audit_status === 'rejected') {
|
||||||
|
ElMessage.error('您的账号已被拒绝,请联系管理员')
|
||||||
|
next('/dashboard')
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果已登录用户访问登录页,重定向到仪表盘
|
// 如果已登录用户访问登录页,重定向到仪表盘
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ export const useUserStore = defineStore('user', {
|
|||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
isAuthenticated: (state) => !!state.token && !!state.user,
|
isAuthenticated: (state) => !!state.token && !!state.user,
|
||||||
isAdmin: (state) => state.user?.role === 'admin'
|
isAdmin: (state) => state.user?.role === 'admin',
|
||||||
|
isSupplier: (state) => state.user?.role === "supplier",
|
||||||
|
isDelete: (state) => state.user?.is_delete === 1,
|
||||||
|
audit_status: (state) => state.user?.audit_status || "pending",
|
||||||
|
|
||||||
|
// isSupplier: (state) => state.user?.user_type === "supplier",
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export const createRequest = (baseURL) => {
|
|||||||
}
|
}
|
||||||
// 生成不同的实例
|
// 生成不同的实例
|
||||||
export const apiRequest = createRequest(import.meta.env.VITE_API_BASE_URL || '/api')
|
export const apiRequest = createRequest(import.meta.env.VITE_API_BASE_URL || '/api')
|
||||||
export const midRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL || '/mid')
|
export const midRequest = createRequest(import.meta.env.VITE_UPLOAD_BASE_URL || 'http://192.168.0.12:3005/mid')
|
||||||
|
|
||||||
|
|
||||||
// API接口定义
|
// API接口定义
|
||||||
@@ -170,7 +170,7 @@ const api = {
|
|||||||
login: (data) => midRequest.post('/auth/login', data),
|
login: (data) => midRequest.post('/auth/login', data),
|
||||||
register: (data) => apiRequest.post('/auth/register', data),
|
register: (data) => apiRequest.post('/auth/register', data),
|
||||||
getCurrentUser: () => apiRequest.get('/auth/me'),
|
getCurrentUser: () => apiRequest.get('/auth/me'),
|
||||||
changePassword: (data) => apiRequest.put('/auth/change-password', data),
|
// changePassword: (data) => apiRequest.put('/auth/change-password', data),
|
||||||
captcha: () => midRequest.get('/captcha/generate'),
|
captcha: () => midRequest.get('/captcha/generate'),
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -184,7 +184,8 @@ const api = {
|
|||||||
getUserStats: () => apiRequest.get('/users/stats'),
|
getUserStats: () => apiRequest.get('/users/stats'),
|
||||||
getUserGrowthTrend: (params) => apiRequest.get('/users/growth-trend', {params}),
|
getUserGrowthTrend: (params) => apiRequest.get('/users/growth-trend', {params}),
|
||||||
getDailyRevenue: (params) => apiRequest.get('/users/daily-revenue', {params}),
|
getDailyRevenue: (params) => apiRequest.get('/users/daily-revenue', {params}),
|
||||||
getAgentOptions: (params) => apiRequest.get('/admin/agents', {params})
|
getAgentOptions: (params) => apiRequest.get('/admin/agents', {params}),
|
||||||
|
changePassword: (data) => apiRequest.put('/users/password', data),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -265,6 +265,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
|||||||
import { ArrowDown } from '@element-plus/icons-vue';
|
import { ArrowDown } from '@element-plus/icons-vue';
|
||||||
import api from '@/utils/api';
|
import api from '@/utils/api';
|
||||||
import { getImageUrl } from '@/utils/config';
|
import { getImageUrl } from '@/utils/config';
|
||||||
|
import { useUserStore } from '@/stores/user';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@@ -272,6 +273,7 @@ const orders = ref([]);
|
|||||||
const dialogVisible = ref(false);
|
const dialogVisible = ref(false);
|
||||||
const deliveryDialogVisible = ref(false);
|
const deliveryDialogVisible = ref(false);
|
||||||
const selectedOrder = ref(null);
|
const selectedOrder = ref(null);
|
||||||
|
const user = useUserStore().user;
|
||||||
|
|
||||||
const filters = reactive({
|
const filters = reactive({
|
||||||
orderNumber: '',
|
orderNumber: '',
|
||||||
@@ -301,6 +303,7 @@ const loadOrders = async () => {
|
|||||||
orderNumber: filters.orderNumber,
|
orderNumber: filters.orderNumber,
|
||||||
username: filters.username,
|
username: filters.username,
|
||||||
status: filters.status,
|
status: filters.status,
|
||||||
|
shop_name: user.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (filters.dateRange && filters.dateRange.length === 2) {
|
if (filters.dateRange && filters.dateRange.length === 2) {
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
size="large"
|
size="large"
|
||||||
clearable
|
clearable
|
||||||
@change="handlePrimaryCategoryChange"
|
@change="handlePrimaryCategoryChange"
|
||||||
|
filterable
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="category in primaryCategories"
|
v-for="category in primaryCategories"
|
||||||
@@ -80,6 +81,7 @@
|
|||||||
multiple
|
multiple
|
||||||
collapse-tags
|
collapse-tags
|
||||||
collapse-tags-tooltip
|
collapse-tags-tooltip
|
||||||
|
filterable
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="category in secondaryCategories"
|
v-for="category in secondaryCategories"
|
||||||
@@ -703,6 +705,9 @@ import MediaUpload from '@/components/MediaUpload.vue'
|
|||||||
import HorizontalImageDisplay from '@/components/HorizontalImageDisplay.vue'
|
import HorizontalImageDisplay from '@/components/HorizontalImageDisplay.vue'
|
||||||
import RichTextEditor from '@/components/RichTextEditor.vue'
|
import RichTextEditor from '@/components/RichTextEditor.vue'
|
||||||
|
|
||||||
|
import {useUserStore} from '@/stores/user'
|
||||||
|
import {getImageUrl} from '@/utils/config'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
@@ -711,6 +716,8 @@ const categories = ref([])
|
|||||||
|
|
||||||
const newProductId = ref(null)
|
const newProductId = ref(null)
|
||||||
|
|
||||||
|
const user = useUserStore().user
|
||||||
|
|
||||||
// 计算属性:一级分类
|
// 计算属性:一级分类
|
||||||
const primaryCategories = computed(() => {
|
const primaryCategories = computed(() => {
|
||||||
return categories.value.filter(cat => cat.level === 1)
|
return categories.value.filter(cat => cat.level === 1)
|
||||||
@@ -755,7 +762,7 @@ const form = reactive({
|
|||||||
images: [],
|
images: [],
|
||||||
videos: [],
|
videos: [],
|
||||||
shop_name: '',
|
shop_name: '',
|
||||||
shop_avatar: '',
|
shop_avatar: getImageUrl(user.avatar),
|
||||||
payment_methods: ['points'],
|
payment_methods: ['points'],
|
||||||
description: '',
|
description: '',
|
||||||
details: '',
|
details: '',
|
||||||
@@ -1171,7 +1178,7 @@ const submitForm = async () => {
|
|||||||
image_url: form.image.replace('https://minio.zrbjr.com', ''), // 前端 image -> 后端 image_url
|
image_url: form.image.replace('https://minio.zrbjr.com', ''), // 前端 image -> 后端 image_url
|
||||||
images: JSON.stringify(form.images.map(img => img.replace('https://minio.zrbjr.com', ''))),
|
images: JSON.stringify(form.images.map(img => img.replace('https://minio.zrbjr.com', ''))),
|
||||||
videos: JSON.stringify(form.videos),
|
videos: JSON.stringify(form.videos),
|
||||||
shop_name: form.shop_name,
|
shop_name: user.id,
|
||||||
shop_avatar: form.shop_avatar,
|
shop_avatar: form.shop_avatar,
|
||||||
payment_methods: JSON.stringify(form.payment_methods),
|
payment_methods: JSON.stringify(form.payment_methods),
|
||||||
description: form.description,
|
description: form.description,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="分类">
|
<el-form-item label="分类">
|
||||||
<el-select v-model="filters.category" placeholder="请选择分类" clearable style="display: inline-block; width: 150px;">
|
<el-select v-model="filters.category" placeholder="请选择分类" clearable style="display: inline-block; width: 150px;" filterable>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="category in categories"
|
v-for="category in categories"
|
||||||
:key="category"
|
:key="category"
|
||||||
@@ -67,11 +67,11 @@
|
|||||||
<span class="rongdou-text">{{ row.rongdou_price || 0 }} 融豆</span>
|
<span class="rongdou-text">{{ row.rongdou_price || 0 }} 融豆</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="shop_name" label="店家" width="120">
|
<el-table-column prop="shop_name" label="供应商" width="120">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<div class="shop-info">
|
<div class="shop-info">
|
||||||
<el-avatar v-if="row.shop_avatar" :src="getImageUrl(row.shop_avatar)" :size="24" />
|
<el-avatar v-if="row.shop_avatar" :src="getImageUrl(row.shop_avatar)" :size="24" />
|
||||||
<span>{{ row.shop_name || '平台上架' }}</span>
|
<span>{{ row.provider ? row.provider.username : '平台上架' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -147,11 +147,13 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
|||||||
import { Plus, ArrowDown, Setting, Grid } from '@element-plus/icons-vue'
|
import { Plus, ArrowDown, Setting, Grid } from '@element-plus/icons-vue'
|
||||||
import api from '@/utils/api'
|
import api from '@/utils/api'
|
||||||
import { getImageUrl } from '@/utils/config'
|
import { getImageUrl } from '@/utils/config'
|
||||||
|
import {useUserStore} from '@/stores/user'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const products = ref([])
|
const products = ref([])
|
||||||
const categories = ref([])
|
const categories = ref([])
|
||||||
|
const user = useUserStore().user
|
||||||
|
|
||||||
const filters = reactive({
|
const filters = reactive({
|
||||||
search: '',
|
search: '',
|
||||||
@@ -170,6 +172,7 @@ const loadProducts = async () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
|
shop_name: user.id,
|
||||||
page: pagination.value.page,
|
page: pagination.value.page,
|
||||||
limit: pagination.value.limit,
|
limit: pagination.value.limit,
|
||||||
...filters
|
...filters
|
||||||
|
|||||||
@@ -394,8 +394,8 @@ const passwordRules = {
|
|||||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||||
{ min: 6, max: 20, message: '密码长度在 6 到 20 个字符', trigger: 'blur' },
|
{ min: 6, max: 20, message: '密码长度在 6 到 20 个字符', trigger: 'blur' },
|
||||||
{
|
{
|
||||||
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]/,
|
pattern: /^(?=.*[a-zA-Z])(?=.*\d)/,
|
||||||
message: '密码必须包含大小写字母和数字',
|
message: '密码必须包含字母和数字',
|
||||||
trigger: 'blur'
|
trigger: 'blur'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -488,7 +488,8 @@ const changePassword = async () => {
|
|||||||
changingPassword.value = true
|
changingPassword.value = true
|
||||||
|
|
||||||
await api.users.changePassword({
|
await api.users.changePassword({
|
||||||
currentPassword: passwordForm.currentPassword,
|
id: userStore.user.id,
|
||||||
|
oldPassword: passwordForm.currentPassword,
|
||||||
newPassword: passwordForm.newPassword
|
newPassword: passwordForm.newPassword
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,8 @@ const loadWithdrawList = async () => {
|
|||||||
try {
|
try {
|
||||||
const { data } = await api.withdraw.getWithdrawList({
|
const { data } = await api.withdraw.getWithdrawList({
|
||||||
page: pagination.page,
|
page: pagination.page,
|
||||||
limit: pagination.limit
|
limit: pagination.limit,
|
||||||
|
user_id: userStore.user.id
|
||||||
})
|
})
|
||||||
withdrawList.value = data.data.withdrawals || []
|
withdrawList.value = data.data.withdrawals || []
|
||||||
pagination.total = data.data.pagination.total || 0
|
pagination.total = data.data.pagination.total || 0
|
||||||
|
|||||||
Reference in New Issue
Block a user