feat: 同步接口更新

This commit is contained in:
郝先瑞 2022-08-02 23:48:23 +08:00
parent 830347ecb0
commit 29a22a9604
22 changed files with 390 additions and 702 deletions

View File

@ -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',
});
}

View File

@ -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,
});
}

View File

@ -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',
});
}

View File

@ -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',

View File

@ -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',
});
}

View File

@ -38,6 +38,7 @@ export const getLanguage = () => {
};
const i18n = createI18n({
legacy: false,
locale: getLanguage(),
messages: messages
});

View File

@ -1,9 +0,0 @@
/**
* Seata表单类型声明
*/
export interface SeataFormData {
openTx: boolean;
stockEx: boolean;
accountEx: boolean;
orderEx: boolean;
}

View File

@ -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;
}

View File

@ -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[];
}

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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);

View File

@ -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(() => {

View File

@ -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>

View File

@ -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('确认删除已选中的数据项?', '警告', {

View File

@ -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(() => {

View File

@ -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('确认删除已选中的数据项?', '警告', {

View File

@ -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>

View File

@ -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>

View File

@ -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 {

View File

@ -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();
}
/**