feat: 同步接口更新
This commit is contained in:
parent
830347ecb0
commit
29a22a9604
|
|
@ -1,36 +0,0 @@
|
|||
import { SeataFormData } from '@/types/api/lab/seata';
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 订单支付
|
||||
* @returns
|
||||
*/
|
||||
export function payOrder(data: SeataFormData) {
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/order/_pay',
|
||||
method: 'post',
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Seata模拟数据(包括订单信息、商品信息、会员余额信息)
|
||||
* @returns
|
||||
*/
|
||||
export function getSeataData() {
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/data',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置Seata模拟数据
|
||||
* @returns
|
||||
*/
|
||||
export function resetSeataData() {
|
||||
return request({
|
||||
url: '/youlai-lab/api/v1/seata/data/_reset',
|
||||
method: 'put',
|
||||
});
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ export function listCategories(queryParams: object) {
|
|||
return request({
|
||||
url: '/mall-pms/api/v1/categories',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ export function listCategories(queryParams: object) {
|
|||
export function listCategoryOptions(): AxiosPromise<Option[]> {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/options',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ export function listCategoryOptions(): AxiosPromise<Option[]> {
|
|||
export function getCategoryDetail(id: number) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ export function addCategory(data: object) {
|
|||
return request({
|
||||
url: '/mall-pms/api/v1/categories',
|
||||
method: 'post',
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ export function updateCategory(id: number, data: object) {
|
|||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ export function updateCategory(id: number, data: object) {
|
|||
export function deleteCategories(ids: string) {
|
||||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + ids,
|
||||
method: 'delete'
|
||||
method: 'delete',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +88,6 @@ export function updateCategoryPart(id: number, data: object) {
|
|||
return request({
|
||||
url: '/mall-pms/api/v1/categories/' + id,
|
||||
method: 'patch',
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import {
|
||||
CouponQueryParam,
|
||||
CouponPageResult,
|
||||
CouponFormData
|
||||
CouponFormData,
|
||||
} from '@/types/api/sms/coupon';
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
|
@ -17,7 +17,7 @@ export function lisCouponPages(
|
|||
return request({
|
||||
url: '/mall-sms/api/v1/coupons/pages',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export function lisCouponPages(
|
|||
export function getCouponFormData(id: number): AxiosPromise<CouponFormData> {
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/coupons/' + id + '/form_data',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ export function addCoupon(data: CouponFormData) {
|
|||
return request({
|
||||
url: '/mall-sms/api/v1/coupons',
|
||||
method: 'post',
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ export function updateCoupon(id: number, data: CouponFormData) {
|
|||
return request({
|
||||
url: '/mall-sms/api/v1/coupons/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +68,6 @@ export function updateCoupon(id: number, data: CouponFormData) {
|
|||
export function deleteCoupons(ids: string) {
|
||||
return request({
|
||||
url: '/mall-sms/api/v1/coupons/' + ids,
|
||||
method: 'delete'
|
||||
method: 'delete',
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
MenuFormData,
|
||||
MenuItem,
|
||||
MenuQueryParam,
|
||||
Resource,
|
||||
} from '@/types/api/system/menu';
|
||||
import { Option } from '@/types/common';
|
||||
import request from '@/utils/request';
|
||||
|
|
@ -43,9 +44,9 @@ export function listMenuOptions(): AxiosPromise<Option[]> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取菜单权限树形列表
|
||||
* 获取资源(菜单+权限)树形列表
|
||||
*/
|
||||
export function getResource(): AxiosPromise<any> {
|
||||
export function listResources(): AxiosPromise<Resource[]> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/resources',
|
||||
method: 'get',
|
||||
|
|
@ -56,7 +57,7 @@ export function getResource(): AxiosPromise<any> {
|
|||
* 获取菜单详情
|
||||
* @param id
|
||||
*/
|
||||
export function getMenuDetail(id: number): AxiosPromise<MenuFormData> {
|
||||
export function getMenuDetail(id: string): AxiosPromise<MenuFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/menus/' + id,
|
||||
method: 'get',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import {
|
|||
RoleFormData,
|
||||
RolePageResult,
|
||||
RoleQueryParam,
|
||||
RoleResourceData
|
||||
RoleResource,
|
||||
} from '@/types/api/system/role';
|
||||
|
||||
import { Option } from '@/types/common';
|
||||
|
|
@ -20,7 +20,7 @@ export function listRolePages(
|
|||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/pages',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ export function listRoleOptions(
|
|||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/options',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -44,10 +44,10 @@ export function listRoleOptions(
|
|||
*
|
||||
* @param queryParams
|
||||
*/
|
||||
export function getRoleResourceIds(roleId: string): AxiosPromise<any> {
|
||||
export function getRoleResources(roleId: string): AxiosPromise<RoleResource> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/resources',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -58,12 +58,12 @@ export function getRoleResourceIds(roleId: string): AxiosPromise<any> {
|
|||
*/
|
||||
export function updateRoleResource(
|
||||
roleId: string,
|
||||
data: RoleResourceData
|
||||
data: RoleResource
|
||||
): AxiosPromise<any> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + roleId + '/resources',
|
||||
method: 'put',
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ export function updateRoleResource(
|
|||
export function getRoleFormDetail(id: number): AxiosPromise<RoleFormData> {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + id,
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ export function addRole(data: RoleFormData) {
|
|||
return request({
|
||||
url: '/youlai-admin/api/v1/roles',
|
||||
method: 'post',
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ export function updateRole(id: number, data: RoleFormData) {
|
|||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + id,
|
||||
method: 'put',
|
||||
data: data
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -114,6 +114,6 @@ export function updateRole(id: number, data: RoleFormData) {
|
|||
export function deleteRoles(ids: string) {
|
||||
return request({
|
||||
url: '/youlai-admin/api/v1/roles/' + ids,
|
||||
method: 'delete'
|
||||
method: 'delete',
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export const getLanguage = () => {
|
|||
};
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: getLanguage(),
|
||||
messages: messages
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
/**
|
||||
* Seata表单类型声明
|
||||
*/
|
||||
export interface SeataFormData {
|
||||
openTx: boolean;
|
||||
stockEx: boolean;
|
||||
accountEx: boolean;
|
||||
orderEx: boolean;
|
||||
}
|
||||
|
|
@ -10,8 +10,9 @@ export interface MenuQueryParam {
|
|||
*/
|
||||
|
||||
export interface MenuItem {
|
||||
id: number;
|
||||
id?: number;
|
||||
parentId: number;
|
||||
type?: string | 'CATEGORY' | 'MENU' | 'EXTLINK';
|
||||
createTime: string;
|
||||
updateTime: string;
|
||||
name: string;
|
||||
|
|
@ -65,3 +66,39 @@ export interface MenuFormData {
|
|||
*/
|
||||
type: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源(菜单+权限)类型
|
||||
*/
|
||||
export interface Resource {
|
||||
/**
|
||||
* 菜单值
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* 菜单文本
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* 子菜单
|
||||
*/
|
||||
children: Resource[];
|
||||
/**
|
||||
* 权限集合
|
||||
*/
|
||||
perms: Permission[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限类型
|
||||
*/
|
||||
export interface Permission {
|
||||
/**
|
||||
* 权限值
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* 权限文本
|
||||
*/
|
||||
label: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { StringMap } from 'i18next';
|
||||
import { PageQueryParam, PageResult } from '../base';
|
||||
|
||||
/**
|
||||
|
|
@ -40,7 +41,7 @@ export interface RoleFormData {
|
|||
/**
|
||||
*
|
||||
*/
|
||||
export interface RoleResourceData {
|
||||
export interface RoleResource {
|
||||
menuIds: string[];
|
||||
permIds: string[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,376 +0,0 @@
|
|||
<!-- setup 无法设置组件名称,组件名称keepAlive必须 -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'seata',
|
||||
};
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted, toRefs } from 'vue';
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
import {
|
||||
Money,
|
||||
Refresh,
|
||||
RefreshLeft,
|
||||
Right,
|
||||
CircleCheckFilled,
|
||||
CircleCloseFilled,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { payOrder, getSeataData, resetSeataData } from '@/api/lab/seata';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { SeataFormData } from '@/types/api/lab/seata';
|
||||
|
||||
const state = reactive({
|
||||
// 保留改变前数据
|
||||
cacheSeataData: {
|
||||
status: undefined,
|
||||
stockNum: undefined,
|
||||
balance: undefined,
|
||||
},
|
||||
seataData: {
|
||||
orderInfo: {
|
||||
orderSn: undefined,
|
||||
status: undefined,
|
||||
},
|
||||
stockInfo: {
|
||||
name: undefined,
|
||||
picUrl: undefined,
|
||||
stockNum: undefined,
|
||||
},
|
||||
accountInfo: {
|
||||
nickName: undefined,
|
||||
avatarUrl: undefined,
|
||||
balance: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
loading: false,
|
||||
|
||||
submitData: {
|
||||
openTx: true, // 是否开启事务
|
||||
orderEx: true, // 订单修改异常
|
||||
} as SeataFormData,
|
||||
});
|
||||
|
||||
const { cacheSeataData, seataData, loading, submitData } = toRefs(state);
|
||||
|
||||
/**
|
||||
* 订单支付(模拟)
|
||||
*/
|
||||
function handleOrderPay() {
|
||||
// 数据校验
|
||||
if (
|
||||
(seataData.value.stockInfo.stockNum &&
|
||||
seataData.value.stockInfo.stockNum != 999) ||
|
||||
(seataData.value.accountInfo.balance &&
|
||||
seataData.value.accountInfo.balance != 1000000000) ||
|
||||
(seataData.value.orderInfo.status &&
|
||||
seataData.value.orderInfo.status != 101)
|
||||
) {
|
||||
ElMessageBox.confirm(
|
||||
'检查到当前数据已被污染,请先重置数据后尝试提交?',
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '重置数据',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
handleDataReset();
|
||||
})
|
||||
.catch(() => {});
|
||||
} else {
|
||||
// 订单支付模拟提交
|
||||
|
||||
loading.value = true;
|
||||
payOrder(submitData.value)
|
||||
.then(() => {
|
||||
ElMessage.success('订单支付成功');
|
||||
})
|
||||
.finally(() => {
|
||||
cacheSeataData.value = {
|
||||
status: seataData.value.orderInfo.status,
|
||||
stockNum: seataData.value.stockInfo.stockNum,
|
||||
balance: seataData.value.accountInfo.balance,
|
||||
};
|
||||
loadData();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载数据
|
||||
*/
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
getSeataData().then((response: any) => {
|
||||
seataData.value = response.data;
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
function handleDataRefresh() {
|
||||
loading.value = true;
|
||||
loadData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据重置
|
||||
*/
|
||||
function handleDataReset() {
|
||||
loading.value = true;
|
||||
resetSeataData().then(() => {
|
||||
ElMessage.success('数据还原成功');
|
||||
loading.value = false;
|
||||
cacheSeataData.value = {
|
||||
status: undefined,
|
||||
stockNum: undefined,
|
||||
balance: undefined,
|
||||
};
|
||||
loadData();
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 第一次加载重置数据测试
|
||||
handleDataReset();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-alert type="info">
|
||||
<p style="font-size: 16px">
|
||||
<b>模拟订单支付流程:</b>
|
||||
扣减商品库存 → 扣减会员余额 → 修改订单状态
|
||||
</p>
|
||||
<p style="font-size: 14px">
|
||||
<b> 分布式事务生效判断:</b>
|
||||
<el-link :icon="CircleCheckFilled" type="success">全部成功</el-link>
|
||||
或
|
||||
<el-link type="danger" :icon="CircleCloseFilled">全部失败</el-link>
|
||||
</p>
|
||||
|
||||
<p style="font-size: 14px">
|
||||
<b> 博客教程:</b>
|
||||
<el-link
|
||||
type="primary"
|
||||
href="https://www.cnblogs.com/haoxianrui/"
|
||||
target="_blank"
|
||||
>
|
||||
https://www.cnblogs.com/haoxianrui</el-link
|
||||
>
|
||||
</p>
|
||||
</el-alert>
|
||||
|
||||
<el-card class="box-card" shadow="always" style="margin-top: 20px">
|
||||
<el-form :inline="true">
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item>
|
||||
<el-switch
|
||||
v-model="submitData.openTx"
|
||||
active-value=""
|
||||
active-text="开启事务"
|
||||
inactive-text="关闭事务"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :icon="Money" @click="handleOrderPay"
|
||||
>订单支付</el-button
|
||||
>
|
||||
<el-button :icon="Refresh" @click="handleDataRefresh"
|
||||
>刷新数据</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="4" style="text-align: right">
|
||||
<el-button :icon="RefreshLeft" @click="handleDataReset"
|
||||
>重置数据</el-button
|
||||
>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-row :gutter="10" style="margin-top: 20px" v-loading="loading">
|
||||
<el-col :span="8" :xs="24" class="card-panel__col">
|
||||
<el-card class="box-card" shadow="always">
|
||||
<template #header>
|
||||
<svg-icon icon-class="goods" />
|
||||
商品信息
|
||||
</template>
|
||||
|
||||
<div style="display: flex">
|
||||
<el-image
|
||||
style="width: 100px; height: 100px"
|
||||
:src="seataData.stockInfo.picUrl"
|
||||
fit="fill"
|
||||
/>
|
||||
<div style="margin-left: 10px">
|
||||
<el-form-item label="商品名称:">
|
||||
{{ seataData.stockInfo.name }}
|
||||
</el-form-item>
|
||||
<el-form-item label="库存数量:" style="display: flex">
|
||||
<div v-if="cacheSeataData.stockNum != null">
|
||||
{{ cacheSeataData.stockNum }} 部
|
||||
<el-icon>
|
||||
<right />
|
||||
</el-icon>
|
||||
</div>
|
||||
|
||||
{{ seataData.stockInfo.stockNum }} 部
|
||||
|
||||
<div v-if="cacheSeataData.stockNum" style="margin-left: 50px">
|
||||
<el-link
|
||||
v-if="
|
||||
cacheSeataData.stockNum != seataData.stockInfo.stockNum
|
||||
"
|
||||
type="success"
|
||||
:underline="false"
|
||||
:icon="CircleCheckFilled"
|
||||
>
|
||||
修改成功
|
||||
</el-link>
|
||||
<el-link
|
||||
v-else-if="
|
||||
cacheSeataData.stockNum == seataData.stockInfo.stockNum
|
||||
"
|
||||
type="danger"
|
||||
:underline="false"
|
||||
:icon="CircleCloseFilled"
|
||||
>
|
||||
修改失败
|
||||
</el-link>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8" :xs="24" class="card-panel__col">
|
||||
<el-card class="box-card" shadow="always">
|
||||
<template #header>
|
||||
<svg-icon icon-class="user" />
|
||||
会员信息
|
||||
</template>
|
||||
|
||||
<div style="display: flex">
|
||||
<el-image
|
||||
style="width: 100px; height: 100px"
|
||||
:src="seataData.accountInfo.avatarUrl"
|
||||
fit="fill"
|
||||
/>
|
||||
<div style="margin-left: 10px">
|
||||
<el-form-item label="会员昵称:">
|
||||
{{ seataData.accountInfo.nickName }}
|
||||
</el-form-item>
|
||||
<el-form-item label="会员余额:">
|
||||
<div v-if="cacheSeataData.balance != null">
|
||||
{{ (cacheSeataData.balance as any) / 100 }} 元
|
||||
<el-icon>
|
||||
<right />
|
||||
</el-icon>
|
||||
</div>
|
||||
{{ (seataData.accountInfo.balance as any) / 100 }} 元
|
||||
|
||||
<div v-if="cacheSeataData.balance" style="margin-left: 50px">
|
||||
<el-link
|
||||
v-if="
|
||||
cacheSeataData.balance != seataData.accountInfo.balance
|
||||
"
|
||||
type="success"
|
||||
:underline="false"
|
||||
:icon="CircleCheckFilled"
|
||||
>
|
||||
修改成功
|
||||
</el-link>
|
||||
<el-link
|
||||
v-else-if="
|
||||
cacheSeataData.balance == seataData.accountInfo.balance
|
||||
"
|
||||
type="danger"
|
||||
:underline="false"
|
||||
:icon="CircleCloseFilled"
|
||||
>
|
||||
修改失败
|
||||
</el-link>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8" :xs="24" class="card-panel__col">
|
||||
<el-card class="box-card" shadow="always">
|
||||
<template #header>
|
||||
<svg-icon icon-class="order" />
|
||||
订单信息
|
||||
<el-checkbox
|
||||
v-model="submitData.orderEx"
|
||||
:label="true"
|
||||
style="float: right; color: #f56c6c"
|
||||
>
|
||||
搞点异常</el-checkbox
|
||||
>
|
||||
</template>
|
||||
<el-form-item label="订单编号:">
|
||||
{{ seataData.orderInfo.orderSn }}
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="订单状态:">
|
||||
<div v-if="cacheSeataData.status == 101">
|
||||
<el-tag type="info"> 待支付 </el-tag>
|
||||
<el-icon>
|
||||
<right />
|
||||
</el-icon>
|
||||
</div>
|
||||
<el-tag v-if="seataData.orderInfo.status == 101" type="info"
|
||||
>待支付</el-tag
|
||||
>
|
||||
<el-tag v-else-if="seataData.orderInfo.status == 201" type="success"
|
||||
>已支付</el-tag
|
||||
>
|
||||
|
||||
<div v-if="cacheSeataData.balance" style="margin-left: 50px">
|
||||
<el-link
|
||||
v-if="cacheSeataData.status != seataData.orderInfo.status"
|
||||
type="success"
|
||||
:underline="false"
|
||||
:icon="CircleCheckFilled"
|
||||
>
|
||||
修改成功
|
||||
</el-link>
|
||||
<el-link
|
||||
v-else
|
||||
type="danger"
|
||||
:underline="false"
|
||||
:icon="CircleCloseFilled"
|
||||
>
|
||||
修改失败
|
||||
</el-link>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-panel__col {
|
||||
margin-bottom: 12px;
|
||||
|
||||
.el-link {
|
||||
font-size: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,111 +1,3 @@
|
|||
<!-- 商品分类层级最多为三层,level字段标识 -->
|
||||
<template>
|
||||
<div class="component-container">
|
||||
<el-tree
|
||||
v-loading="loading"
|
||||
ref="categoryTreeRef"
|
||||
:data="categoryOptions"
|
||||
:props="{ label: 'name', children: 'children', disabled: '' }"
|
||||
node-key="id"
|
||||
:expand-on-click-node="false"
|
||||
default-expand-all
|
||||
:accordion="true"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<template #default="scope">
|
||||
<div class="custom-tree-node">
|
||||
<span>
|
||||
<el-image
|
||||
v-show="scope.data.level == 3"
|
||||
:src="scope.data.iconUrl"
|
||||
style="
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
margin-top: -5px;
|
||||
"
|
||||
>
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<Picture style="width: 20px; height: 20px" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
{{ scope.data.name }}
|
||||
</span>
|
||||
<span>
|
||||
<el-button
|
||||
v-show="scope.data.level != 3"
|
||||
type="success"
|
||||
:icon="Plus"
|
||||
circle
|
||||
plain
|
||||
@click.stop="handleAdd(scope.data)"
|
||||
/>
|
||||
<el-button
|
||||
v-show="scope.data.id !== 0"
|
||||
type="warning"
|
||||
:icon="Edit"
|
||||
circle
|
||||
plain
|
||||
@click.stop="handleUpdate(scope.data)"
|
||||
/>
|
||||
<el-button
|
||||
v-show="
|
||||
scope.data.id &&
|
||||
(!scope.data.children || scope.data.children.length <= 0)
|
||||
"
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
circle
|
||||
plain
|
||||
@click.stop="handleDelete(scope.data)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="750px">
|
||||
<el-form
|
||||
ref="dataFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="上级分类" prop="parentId">
|
||||
<el-input v-model="parent.name" readonly />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="分类名称" prop="name">
|
||||
<el-input v-model="formData.name" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="分类图标" prop="iconUrl">
|
||||
<single-upload v-model="formData.iconUrl" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="显示状态" prop="visible">
|
||||
<el-radio-group v-model="formData.visible">
|
||||
<el-radio :label="1">显示</el-radio>
|
||||
<el-radio :label="0">隐藏</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input v-model="formData.sort"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
listCategories,
|
||||
|
|
@ -268,6 +160,114 @@ onMounted(() => {
|
|||
});
|
||||
</script>
|
||||
|
||||
<!-- 商品分类层级最多为三层,level字段标识 -->
|
||||
<template>
|
||||
<div class="component-container">
|
||||
<el-tree
|
||||
v-loading="loading"
|
||||
ref="categoryTreeRef"
|
||||
:data="categoryOptions"
|
||||
:props="{ label: 'name', children: 'children', disabled: '' }"
|
||||
node-key="id"
|
||||
:expand-on-click-node="false"
|
||||
default-expand-all
|
||||
:accordion="true"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
<template #default="scope">
|
||||
<div class="custom-tree-node">
|
||||
<span>
|
||||
<el-image
|
||||
v-show="scope.data.level == 3"
|
||||
:src="scope.data.iconUrl"
|
||||
style="
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
margin-top: -5px;
|
||||
"
|
||||
>
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<Picture style="width: 20px; height: 20px" />
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
{{ scope.data.name }}
|
||||
</span>
|
||||
<span>
|
||||
<el-button
|
||||
v-show="scope.data.level != 3"
|
||||
type="success"
|
||||
:icon="Plus"
|
||||
circle
|
||||
plain
|
||||
@click.stop="handleAdd(scope.data)"
|
||||
/>
|
||||
<el-button
|
||||
v-show="scope.data.id !== 0"
|
||||
type="warning"
|
||||
:icon="Edit"
|
||||
circle
|
||||
plain
|
||||
@click.stop="handleUpdate(scope.data)"
|
||||
/>
|
||||
<el-button
|
||||
v-show="
|
||||
scope.data.id &&
|
||||
(!scope.data.children || scope.data.children.length <= 0)
|
||||
"
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
circle
|
||||
plain
|
||||
@click.stop="handleDelete(scope.data)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="750px">
|
||||
<el-form
|
||||
ref="dataFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="上级分类" prop="parentId">
|
||||
<el-input v-model="parent.name" readonly />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="分类名称" prop="name">
|
||||
<el-input v-model="formData.name" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="分类图标" prop="iconUrl">
|
||||
<single-upload v-model="formData.iconUrl" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="显示状态" prop="visible">
|
||||
<el-radio-group v-model="formData.visible">
|
||||
<el-radio :label="1">显示</el-radio>
|
||||
<el-radio :label="0">隐藏</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input v-model="formData.sort"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.component-container {
|
||||
height: 100%;
|
||||
|
|
@ -280,13 +280,10 @@ onMounted(() => {
|
|||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
line-height: 40px0;
|
||||
}
|
||||
|
||||
.el-tree-node__content {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.el-divider--horizontal {
|
||||
margin: 30px 0 15px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -49,20 +49,20 @@ const emit = defineEmits(['next', 'update:modelValue']);
|
|||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const goodsInfo: any = computed({
|
||||
get: () => props.modelValue,
|
||||
set: value => {
|
||||
set: (value) => {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
categoryOptions: [] as Option[],
|
||||
pathLabels: []
|
||||
pathLabels: [],
|
||||
});
|
||||
|
||||
const { categoryOptions, pathLabels } = toRefs(state);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<!-- setup 无法设置组件名称,组件名称keepAlive必须 -->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'goods'
|
||||
name: 'goods',
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ import {
|
|||
Edit,
|
||||
Refresh,
|
||||
Delete,
|
||||
View
|
||||
View,
|
||||
} from '@element-plus/icons-vue';
|
||||
import { listSpuPages, deleteSpu } from '@/api/pms/goods';
|
||||
import { listCategoryOptions } from '@/api/pms/category';
|
||||
|
|
@ -39,12 +39,12 @@ const state = reactive({
|
|||
total: 0,
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
pageSize: 10,
|
||||
} as GoodsQueryParam,
|
||||
goodsList: [] as GoodsItem[],
|
||||
categoryOptions: [] as Option[],
|
||||
goodDetail: undefined,
|
||||
dialogVisible: false
|
||||
dialogVisible: false,
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -55,7 +55,7 @@ const {
|
|||
categoryOptions,
|
||||
goodDetail,
|
||||
total,
|
||||
dialogVisible
|
||||
dialogVisible,
|
||||
} = toRefs(state);
|
||||
|
||||
function handleQuery() {
|
||||
|
|
@ -72,7 +72,7 @@ function resetQuery() {
|
|||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
categoryId: undefined
|
||||
categoryId: undefined,
|
||||
};
|
||||
handleQuery();
|
||||
}
|
||||
|
|
@ -89,7 +89,7 @@ function handleAdd() {
|
|||
function handleUpdate(row: any) {
|
||||
router.push({
|
||||
path: 'goods-detail',
|
||||
query: { goodsId: row.id, categoryId: row.categoryId }
|
||||
query: { goodsId: row.id, categoryId: row.categoryId },
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ function handleDelete(row: any) {
|
|||
ElMessageBox.confirm('是否确认删除选中的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
type: 'warning',
|
||||
})
|
||||
.then(function () {
|
||||
return deleteSpu(ids);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
export default {
|
||||
name: 'advert'
|
||||
name: 'advert',
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -14,17 +14,17 @@ import {
|
|||
getAdvertFormDetail,
|
||||
updateAdvert,
|
||||
addAdvert,
|
||||
deleteAdverts
|
||||
deleteAdverts,
|
||||
} from '@/api/sms/advert';
|
||||
import { Dialog } from '@/types/common';
|
||||
import {
|
||||
AdvertFormData,
|
||||
AdvertItem,
|
||||
AdvertQueryParam
|
||||
AdvertQueryParam,
|
||||
} from '@/types/api/sms/advert';
|
||||
|
||||
const queryFormRef = ref(ElForm);
|
||||
const dataFormRef = ref(ElForm);
|
||||
const queryFormRef = ref(ElForm); // 属性名必须和元素的ref属性值一致
|
||||
const dataFormRef = ref(ElForm); // 属性名必须和元素的ref属性值一致
|
||||
|
||||
const state = reactive({
|
||||
loading: true,
|
||||
|
|
@ -40,13 +40,13 @@ const state = reactive({
|
|||
dialog: { title: '', visible: false } as Dialog,
|
||||
formData: {
|
||||
status: 1,
|
||||
sort: 100
|
||||
sort: 100,
|
||||
} as AdvertFormData,
|
||||
rules: {
|
||||
title: [{ required: true, message: '请输入广告名称', trigger: 'blur' }],
|
||||
picUrl: [{ required: true, message: '请上传广告图片', trigger: 'blur' }]
|
||||
picUrl: [{ required: true, message: '请上传广告图片', trigger: 'blur' }],
|
||||
},
|
||||
validityPeriod: '' as any
|
||||
validityPeriod: '' as any,
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -58,7 +58,7 @@ const {
|
|||
dialog,
|
||||
formData,
|
||||
rules,
|
||||
validityPeriod
|
||||
validityPeriod,
|
||||
} = toRefs(state);
|
||||
|
||||
function handleQuery() {
|
||||
|
|
@ -84,14 +84,14 @@ function handleSelectionChange(selection: any) {
|
|||
function handleAdd() {
|
||||
state.dialog = {
|
||||
title: '添加广告',
|
||||
visible: true
|
||||
visible: true,
|
||||
};
|
||||
}
|
||||
|
||||
function handleUpdate(row: any) {
|
||||
state.dialog = {
|
||||
title: '修改广告',
|
||||
visible: true
|
||||
visible: true,
|
||||
};
|
||||
const advertId = row.id || state.ids;
|
||||
getAdvertFormDetail(advertId).then(({ data }) => {
|
||||
|
|
@ -138,7 +138,7 @@ function handleDelete(row: any) {
|
|||
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
deleteAdverts(ids).then(() => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<!--优惠券-->
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'coupon'
|
||||
name: 'coupon',
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ import {
|
|||
getCouponFormData,
|
||||
updateCoupon,
|
||||
addCoupon,
|
||||
deleteCoupons
|
||||
deleteCoupons,
|
||||
} from '@/api/sms/coupon';
|
||||
|
||||
import { listCategoryOptions } from '@/api/pms/category';
|
||||
|
|
@ -23,7 +23,7 @@ import { Dialog, Option } from '@/types/common';
|
|||
import {
|
||||
CouponItem,
|
||||
CouponQueryParam,
|
||||
CouponFormData
|
||||
CouponFormData,
|
||||
} from '@/types/api/sms/coupon';
|
||||
import { GoodsItem, GoodsQueryParam } from '@/types/api/pms/goods';
|
||||
|
||||
|
|
@ -40,38 +40,38 @@ const state = reactive({
|
|||
couponList: [] as CouponItem[],
|
||||
total: 0,
|
||||
dialog: {
|
||||
visible: false
|
||||
visible: false,
|
||||
} as Dialog,
|
||||
//指定商品分类选择Dialog
|
||||
spuCategoryChooseDialog: {
|
||||
visible: false
|
||||
visible: false,
|
||||
} as Dialog,
|
||||
// 指定商品选择ialog
|
||||
spuChooseDialog: {
|
||||
visible: false
|
||||
visible: false,
|
||||
} as Dialog,
|
||||
formData: {
|
||||
type: 1,
|
||||
platform: 0,
|
||||
validityPeriodType: 1,
|
||||
perLimit: 1,
|
||||
applicationScope: 0
|
||||
applicationScope: 0,
|
||||
} as CouponFormData,
|
||||
rules: {
|
||||
type: [{ required: true, message: '请输入优惠券名称', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '请选择优惠券类型', trigger: 'blur' }]
|
||||
name: [{ required: true, message: '请选择优惠券类型', trigger: 'blur' }],
|
||||
},
|
||||
validityPeriod: '' as any,
|
||||
perLimitChecked: false,
|
||||
spuCategoryOptions: [] as Option[],
|
||||
spuCategoryProps: {
|
||||
multiple: true,
|
||||
emitPath: false
|
||||
emitPath: false,
|
||||
},
|
||||
spuList: [] as GoodsItem[],
|
||||
spuTotal: 0,
|
||||
spuQueryParams: { pageNum: 1, pageSize: 10 } as GoodsQueryParam,
|
||||
checkedSpuIds: []
|
||||
checkedSpuIds: [],
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -89,7 +89,7 @@ const {
|
|||
spuCategoryProps,
|
||||
spuList,
|
||||
spuTotal,
|
||||
checkedSpuIds
|
||||
checkedSpuIds,
|
||||
} = toRefs(state);
|
||||
|
||||
/**
|
||||
|
|
@ -136,7 +136,7 @@ async function loadSpuList() {
|
|||
function handleAdd() {
|
||||
dialog.value = {
|
||||
title: '新增优惠券',
|
||||
visible: true
|
||||
visible: true,
|
||||
};
|
||||
|
||||
loadSpuCategoryOptions();
|
||||
|
|
@ -146,7 +146,7 @@ function handleAdd() {
|
|||
async function handleUpdate(row: any) {
|
||||
dialog.value = {
|
||||
title: '编辑优惠券',
|
||||
visible: true
|
||||
visible: true,
|
||||
};
|
||||
const id = row.id;
|
||||
|
||||
|
|
@ -230,7 +230,7 @@ function handleDelete(row: any) {
|
|||
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
deleteCoupons(ids).then(() => {
|
||||
|
|
@ -492,7 +492,7 @@ onMounted(() => {
|
|||
:titles="['商品列表', '已选择商品']"
|
||||
:props="{
|
||||
key: 'id',
|
||||
label: 'name'
|
||||
label: 'name',
|
||||
}"
|
||||
>
|
||||
<template #left-footer>
|
||||
|
|
|
|||
|
|
@ -142,16 +142,12 @@ function submitForm() {
|
|||
});
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
function cancel() {
|
||||
state.dialog.visible = false;
|
||||
dataFormRef.value.resetFields();
|
||||
state.checkedAuthorizedGrantTypes = [];
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
resetForm();
|
||||
state.dialog.visible = false;
|
||||
}
|
||||
|
||||
function handleDelete(row: any) {
|
||||
const clientIds = [row.clientId || ids].join(',');
|
||||
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', {
|
||||
|
|
|
|||
|
|
@ -192,8 +192,10 @@ function handleDelete(row: any) {
|
|||
* 取消/关闭弹窗
|
||||
**/
|
||||
function cancel() {
|
||||
dialog.value.visible = false;
|
||||
formData.value.id = undefined;
|
||||
dataFormRef.value.resetFields();
|
||||
state.dialog.visible = false;
|
||||
dataFormRef.value.clearValidate();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,16 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="菜单类型" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.type === 'MENU'" type="success">菜单</el-tag>
|
||||
<el-tag v-if="scope.row.type === 'CATALOG'" type="warning"
|
||||
>目录</el-tag
|
||||
>
|
||||
<el-tag v-if="scope.row.type === 'EXTLINK'" type="info">外链</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.visible === 1" type="success">显示</el-tag>
|
||||
|
|
@ -261,7 +271,7 @@ const state = reactive({
|
|||
name: '',
|
||||
visible: 1,
|
||||
sort: 1,
|
||||
component: 'Layout',
|
||||
component: undefined,
|
||||
type: 'MENU',
|
||||
} as MenuFormData,
|
||||
rules: {
|
||||
|
|
@ -333,8 +343,12 @@ function handleRowClick(row: any) {
|
|||
emit('menuClick', row);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增菜单
|
||||
* @param row
|
||||
*/
|
||||
async function handleAdd(row: any) {
|
||||
state.formData.id = undefined;
|
||||
formData.value.id = undefined;
|
||||
await loadMenuData();
|
||||
state.dialog = {
|
||||
title: '添加菜单',
|
||||
|
|
@ -342,7 +356,6 @@ async function handleAdd(row: any) {
|
|||
};
|
||||
if (row.id) {
|
||||
// 行点击新增
|
||||
|
||||
state.formData.parentId = row.id;
|
||||
if (row.id == '0') {
|
||||
state.formData.type = 'CATALOG';
|
||||
|
|
@ -362,15 +375,15 @@ async function handleAdd(row: any) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 修改弹窗
|
||||
* 编辑菜单
|
||||
*/
|
||||
async function handleUpdate(row: any) {
|
||||
async function handleUpdate(row: MenuFormData) {
|
||||
await loadMenuData();
|
||||
state.dialog = {
|
||||
title: '修改菜单',
|
||||
title: '编辑菜单',
|
||||
visible: true,
|
||||
};
|
||||
const id = row.id || state.ids;
|
||||
const id = row.id as string;
|
||||
getMenuDetail(id).then(({ data }) => {
|
||||
state.formData = data;
|
||||
cacheData.value.menuType = data.type;
|
||||
|
|
@ -379,10 +392,10 @@ async function handleUpdate(row: any) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 菜单类型change事件
|
||||
* 菜单类型 change
|
||||
*/
|
||||
function handleMenuTypeChange(val: any) {
|
||||
if (val !== cacheData.value.menuType) {
|
||||
function handleMenuTypeChange(menuType: any) {
|
||||
if (menuType !== cacheData.value.menuType) {
|
||||
formData.value.path = '';
|
||||
} else {
|
||||
formData.value.path = cacheData.value.menuPath;
|
||||
|
|
@ -412,6 +425,11 @@ function submitForm() {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
*
|
||||
* @param row
|
||||
*/
|
||||
function handleDelete(row: any) {
|
||||
const ids = [row.id || state.ids].join(',');
|
||||
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
PermItem,
|
||||
PermQueryParam,
|
||||
} from '@/types/api/system/perm';
|
||||
import { MenuItem } from '@/types/api/system/menu';
|
||||
|
||||
const { proxy }: any = getCurrentInstance();
|
||||
|
||||
|
|
@ -32,19 +33,23 @@ const queryFormRef = ref(ElForm);
|
|||
const dataFormRef = ref(ElForm);
|
||||
|
||||
const props = defineProps({
|
||||
menuId: {
|
||||
type: String,
|
||||
menu: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return '';
|
||||
return {} as MenuItem;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.menuId,
|
||||
() => props.menu,
|
||||
(value) => {
|
||||
state.queryParams.menuId = value;
|
||||
queryParams.value.menuId = value.id;
|
||||
console.log('menu', value);
|
||||
handleQuery();
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -193,7 +198,7 @@ function submitForm() {
|
|||
state.urlPerm.requestPath;
|
||||
}
|
||||
|
||||
state.formData.menuId = props.menuId;
|
||||
formData.value.menuId = props.menu.id;
|
||||
if (state.formData.id) {
|
||||
updatePerm(state.formData.id, state.formData).then(() => {
|
||||
ElMessage.success('修改成功');
|
||||
|
|
@ -258,7 +263,7 @@ onMounted(() => {
|
|||
<el-button
|
||||
type="success"
|
||||
:icon="Plus"
|
||||
:disabled="!menuId"
|
||||
v-if="menu.id && menu.type == 'MENU'"
|
||||
@click="handleAdd"
|
||||
>新增</el-button
|
||||
>
|
||||
|
|
@ -267,6 +272,7 @@ onMounted(() => {
|
|||
:icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete"
|
||||
v-if="menu.id && menu.type == 'MENU'"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@
|
|||
<template #header>
|
||||
<svg-icon icon-class="perm" />
|
||||
<span style="margin: 0 5px">权限列表</span>
|
||||
<el-tag type="success" v-if="menuId" size="small">{{
|
||||
menuName
|
||||
<el-tag type="success" v-if="menu.id" size="small">{{
|
||||
menu.name
|
||||
}}</el-tag>
|
||||
<el-tag type="warning" v-else size="small"
|
||||
>请点击左侧菜单列表选择</el-tag
|
||||
<el-link :underline="false" type="warning" v-else size="small"
|
||||
><el-icon><WarningFilled /></el-icon>请选中左侧菜单</el-link
|
||||
>
|
||||
</template>
|
||||
<perm-table :menuId="menuId" :menuName="menuName" />
|
||||
<perm-table :menu="menu" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
@ -35,21 +35,23 @@ import MenuTable from './components/Menu.vue';
|
|||
import PermTable from './components/Perm.vue';
|
||||
|
||||
import { reactive, toRefs } from 'vue';
|
||||
|
||||
import { WarningFilled } from '@element-plus/icons-vue';
|
||||
import { MenuItem } from '@/types/api/system/menu';
|
||||
const state = reactive({
|
||||
menuId: undefined,
|
||||
menuName: '',
|
||||
menu: {} as MenuItem,
|
||||
});
|
||||
|
||||
const { menuId, menuName } = toRefs(state);
|
||||
const { menu } = toRefs(state);
|
||||
|
||||
function handleMenuClick(menuRow: any) {
|
||||
function handleMenuClick(menuRow: MenuItem) {
|
||||
if (menuRow) {
|
||||
state.menuId = menuRow.id;
|
||||
state.menuName = menuRow.name;
|
||||
menu.value.id = menuRow.id;
|
||||
menu.value.type = menuRow.type;
|
||||
menu.value.name = menuRow.name;
|
||||
} else {
|
||||
state.menuId = undefined;
|
||||
state.menuName = '';
|
||||
menu.value.id = undefined;
|
||||
menu.value.type = undefined;
|
||||
menu.value.name = '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -5,17 +5,17 @@ export default {
|
|||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref, toRefs, nextTick } from 'vue';
|
||||
import { nextTick, onMounted, reactive, ref, toRefs } from 'vue';
|
||||
import {
|
||||
listRolePages,
|
||||
updateRole,
|
||||
getRoleFormDetail,
|
||||
addRole,
|
||||
deleteRoles,
|
||||
getRoleResourceIds,
|
||||
getRoleResources,
|
||||
updateRoleResource,
|
||||
} from '@/api/system/role';
|
||||
import { getResource } from '@/api/system/menu';
|
||||
import { listResources } from '@/api/system/menu';
|
||||
|
||||
import { ElForm, ElMessage, ElMessageBox, ElTree } from 'element-plus';
|
||||
import { Search, Plus, Edit, Refresh, Delete } from '@element-plus/icons-vue';
|
||||
|
|
@ -24,6 +24,7 @@ import {
|
|||
RoleItem,
|
||||
RoleQueryParam,
|
||||
} from '@/types/api/system/role';
|
||||
import { Resource } from '@/types/api/system/menu';
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||
|
||||
const emit = defineEmits(['roleClick']);
|
||||
|
|
@ -55,8 +56,11 @@ const state = reactive({
|
|||
code: [{ required: true, message: '请输入角色编码', trigger: 'blur' }],
|
||||
},
|
||||
resourceDialogVisible: false,
|
||||
menuOptions: [] as any[],
|
||||
permOptions: [] as any[],
|
||||
resourceOptions: [] as Resource[],
|
||||
btnPerms: {} as any,
|
||||
// 勾选的菜单ID
|
||||
checkedMenuIds: new Set([]),
|
||||
allPermIds: [] as string[],
|
||||
checkedRole: {
|
||||
id: '',
|
||||
name: '',
|
||||
|
|
@ -73,9 +77,9 @@ const {
|
|||
formData,
|
||||
rules,
|
||||
resourceDialogVisible,
|
||||
menuOptions,
|
||||
permOptions,
|
||||
checkedRole,
|
||||
resourceOptions,
|
||||
btnPerms,
|
||||
} = toRefs(state);
|
||||
|
||||
function handleQuery() {
|
||||
|
|
@ -87,7 +91,9 @@ function handleQuery() {
|
|||
state.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询重置
|
||||
*/
|
||||
function resetQuery() {
|
||||
queryFormRef.value.resetFields();
|
||||
handleQuery();
|
||||
|
|
@ -148,8 +154,10 @@ function submitFormData() {
|
|||
* 取消
|
||||
*/
|
||||
function cancel() {
|
||||
state.dialog.visible = false;
|
||||
dialog.value.visible = false;
|
||||
formData.value.id = undefined;
|
||||
dataFormRef.value.resetFields();
|
||||
dataFormRef.value.clearValidate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -171,13 +179,26 @@ function handleDelete(row: any) {
|
|||
.catch(() => ElMessage.info('已取消删除'));
|
||||
}
|
||||
|
||||
const handleResourceCheckChange = (
|
||||
data: Resource,
|
||||
isCheck: boolean,
|
||||
sonHasCheck: boolean
|
||||
) => {
|
||||
console.log('data', data);
|
||||
console.log('isCheck', isCheck);
|
||||
if (data.perms) {
|
||||
data.perms.forEach((item) => {
|
||||
btnPerms.value[item.value] = isCheck;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 分配资源权限
|
||||
* 分配资源(菜单+权限)弹窗
|
||||
*/
|
||||
function handleResourceAssign(row: RoleItem) {
|
||||
function openRoleResourceDialog(row: RoleItem) {
|
||||
resourceDialogVisible.value = true;
|
||||
loading.value = true;
|
||||
permOptions.value.map((item) => (item.checked = false));
|
||||
|
||||
const roleId: any = row.id;
|
||||
checkedRole.value = {
|
||||
|
|
@ -185,47 +206,72 @@ function handleResourceAssign(row: RoleItem) {
|
|||
name: row.name,
|
||||
};
|
||||
|
||||
//资源下拉数据
|
||||
getResource().then((response) => {
|
||||
state.menuOptions = response.data.menus;
|
||||
state.permOptions = response.data.perms;
|
||||
// 获取所有的资源
|
||||
listResources().then((response) => {
|
||||
resourceOptions.value = response.data;
|
||||
|
||||
// 获取角色拥有的资源数据进行勾选
|
||||
getRoleResourceIds(roleId).then((res) => {
|
||||
const checkedMenuIds = res.data.menuIds;
|
||||
const checkedPermIds = res.data.permIds;
|
||||
// 获取角色拥有的资源
|
||||
getRoleResources(roleId).then(({ data }) => {
|
||||
// 勾选的菜单回显
|
||||
const checkedMenuIds = data.menuIds;
|
||||
resourceRef.value.setCheckedKeys(checkedMenuIds);
|
||||
|
||||
permOptions.value.forEach((perm) => {
|
||||
if (checkedPermIds.includes(perm.value)) {
|
||||
perm.checked = true;
|
||||
} else {
|
||||
perm.checked = false;
|
||||
nextTick(() => {
|
||||
// 勾选的权限回显
|
||||
const rolePermIds = data.permIds;
|
||||
|
||||
state.allPermIds = filterResourcePermIds(response.data, []);
|
||||
if (state.allPermIds) {
|
||||
state.allPermIds.forEach((permId) => {
|
||||
if (rolePermIds.indexOf(permId) > -1) {
|
||||
btnPerms.value[permId] = true;
|
||||
} else {
|
||||
btnPerms.value[permId] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const filterResourcePermIds = (resources: Resource[], permIds: string[]) => {
|
||||
resources.forEach((resource) => {
|
||||
if (resource.perms) {
|
||||
resource.perms.forEach((perm) => {
|
||||
permIds.push(perm.value);
|
||||
});
|
||||
}
|
||||
if (resource.children) {
|
||||
filterResourcePermIds(resource.children, permIds);
|
||||
}
|
||||
});
|
||||
return permIds;
|
||||
};
|
||||
/**
|
||||
* 分配资源权限提交
|
||||
* 分配资源提交
|
||||
*/
|
||||
function handleRoleResourceSubmit() {
|
||||
const checkedMenuIds: any[] = resourceRef.value
|
||||
.getCheckedNodes(false, true)
|
||||
.map((node: any) => node.value);
|
||||
|
||||
const checkedPermIds = state.permOptions
|
||||
.filter((item) => item.checked)
|
||||
.map((item) => item.value);
|
||||
const checkedPermIds = [] as string[];
|
||||
if (state.allPermIds) {
|
||||
state.allPermIds.forEach((permId) => {
|
||||
if (btnPerms.value[permId]) {
|
||||
checkedPermIds.push(permId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const roleResourceData = {
|
||||
const RoleResource = {
|
||||
menuIds: checkedMenuIds,
|
||||
permIds: checkedPermIds,
|
||||
};
|
||||
|
||||
updateRoleResource(checkedRole.value.id, roleResourceData).then((res) => {
|
||||
updateRoleResource(checkedRole.value.id, RoleResource).then((res) => {
|
||||
ElMessage.success('分配权限成功');
|
||||
state.resourceDialogVisible = false;
|
||||
handleQuery();
|
||||
|
|
@ -291,14 +337,16 @@ onMounted(() => {
|
|||
<el-table-column label="角色编码" prop="code" />
|
||||
<el-table-column label="操作" align="center" width="200">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
circle
|
||||
plain
|
||||
@click.stop="handleResourceAssign(scope.row)"
|
||||
>
|
||||
<svg-icon icon-class="perm" />
|
||||
</el-button>
|
||||
<el-tooltip content="分配资源" effect="light">
|
||||
<el-button
|
||||
type="success"
|
||||
circle
|
||||
plain
|
||||
@click.stop="openRoleResourceDialog(scope.row)"
|
||||
>
|
||||
<svg-icon icon-class="perm" />
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-button
|
||||
type="primary"
|
||||
|
|
@ -373,37 +421,38 @@ onMounted(() => {
|
|||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!--分配权限弹窗-->
|
||||
<!--分配资源弹窗-->
|
||||
<el-dialog
|
||||
:title="'【' + checkedRole.name + '】分配权限'"
|
||||
:title="'角色【' + checkedRole.name + '】资源分配'"
|
||||
v-model="resourceDialogVisible"
|
||||
width="1000px"
|
||||
width="800px"
|
||||
>
|
||||
<el-scrollbar max-height="600px" v-loading="loading">
|
||||
<el-tree
|
||||
ref="resourceRef"
|
||||
node-key="value"
|
||||
show-checkbox
|
||||
:data="menuOptions"
|
||||
:data="resourceOptions"
|
||||
:default-expand-all="true"
|
||||
@check-change="handleResourceCheckChange"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<div v-if="data.isPerm == true" class="resource-tree-node">
|
||||
<div class="resource-tree-node__content">
|
||||
<template #default="{ data }">
|
||||
{{ data.label }}
|
||||
|
||||
<div v-if="data.perms" class="resource-tree-node">
|
||||
<el-divider direction="vertical" />
|
||||
<div class="node-content">
|
||||
<el-checkbox
|
||||
v-for="perm in permOptions.filter(
|
||||
(perm) => perm.parentId == data.permPid
|
||||
)"
|
||||
v-for="perm in data.perms"
|
||||
:key="perm.value"
|
||||
:label="perm.value"
|
||||
v-model="perm.checked"
|
||||
border
|
||||
size="small"
|
||||
v-model="btnPerms[perm.value]"
|
||||
>{{ perm.label }}</el-checkbox
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else>{{ node.label }}</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
|
|
@ -425,18 +474,16 @@ onMounted(() => {
|
|||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
margin-left: -28px !important;
|
||||
|
||||
&__content {
|
||||
justify-content: flex-end;
|
||||
margin: 0 50px;
|
||||
.node-content {
|
||||
width: 400px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.el-checkbox--default {
|
||||
background-color: transparent !important;
|
||||
.el-divider--vertical {
|
||||
height: 2em !important;
|
||||
}
|
||||
}
|
||||
.el-tree-node__content {
|
||||
|
|
|
|||
|
|
@ -348,9 +348,10 @@ function handleDelete(row: { [key: string]: any }) {
|
|||
* 取消
|
||||
*/
|
||||
function cancel() {
|
||||
state.dialog.visible = false;
|
||||
state.formData.id = undefined;
|
||||
dialog.value.visible = false;
|
||||
formData.value.id = undefined;
|
||||
dataFormRef.value.resetFields();
|
||||
dataFormRef.value.clearValidate();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue