refactor: keepalive无效问题修复和代码vue社区代码规范调整

Former-commit-id: f661982d54f1738ff9739f1afc993181a466f052
This commit is contained in:
郝先瑞
2022-04-24 00:08:25 +08:00
parent c0b96bfab8
commit 47ed525fcd
71 changed files with 2814 additions and 1912 deletions

View File

@@ -0,0 +1,316 @@
<!-- 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";
const state = reactive({
// 保留改变前数据
cacheSeataData: {
status: undefined,
stockNum: undefined,
balance: undefined
},
seataData: {
orderInfo: {
orderSn: undefined,
status: undefined
},
skuInfo: {
name: undefined,
picUrl: undefined,
stockNum: undefined
},
memberInfo: {
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.skuInfo.stockNum && seataData.value.skuInfo.stockNum != 999
|| (seataData.value.memberInfo.balance && seataData.value.memberInfo.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.skuInfo.stockNum,
balance: seataData.value.memberInfo.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.skuInfo.picUrl" fit="fill" />
<div style="margin-left: 10px;">
<el-form-item label="商品名称:">
{{ seataData.skuInfo.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.skuInfo.stockNum }}
<div v-if="cacheSeataData.stockNum" style="margin-left: 50px;">
<el-link v-if="cacheSeataData.stockNum != seataData.skuInfo.stockNum" type="success"
:underline="false" :icon="CircleCheckFilled">
修改成功
</el-link>
<el-link v-else-if="cacheSeataData.stockNum == seataData.skuInfo.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.memberInfo.avatarUrl"
fit="fill" />
<div style="margin-left: 10px;">
<el-form-item label="会员昵称:">
{{ seataData.memberInfo.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.memberInfo.balance as any) / 100 }}
<div v-if="cacheSeataData.balance" style="margin-left: 50px;">
<el-link v-if="cacheSeataData.balance != seataData.memberInfo.balance"
type="success" :underline="false" :icon="CircleCheckFilled">
修改成功
</el-link>
<el-link v-else-if="cacheSeataData.balance == seataData.memberInfo.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,3 +1,122 @@
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "order"
};
</script>
<script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue";
import { ElForm } from "element-plus";
import { Dialog, Order, OrderQueryParam } from "@/types";
import { listOrderPages, getOrderDetail } from "@/api/oms/order";
import { Search, Refresh } from "@element-plus/icons-vue";
const queryFormRef = ref(ElForm);
const orderSourceMap = {
1: "微信小程序",
2: "APP",
3: "PC",
};
const orderStatusMap = {
101: "待付款",
102: "用户取消",
103: "系统取消",
201: "已付款",
202: "申请退款",
203: "已退款",
301: "待发货",
401: "已发货",
501: "用户收货",
502: "系统收货",
901: "已完成",
};
const payTypeMap = {
1: "支付宝",
2: "微信",
3: "会员余额",
};
const state = reactive({
loading: false,
ids: [],
single: true,
multiple: true,
dateRange: [],
queryParams: {
pageNum: 1,
pageSize: 10,
} as OrderQueryParam,
orderList: [] as Order[],
total: 0,
dialog: {
title: "订单详情",
visible: false,
} as Dialog,
dialogVisible: false,
orderDetail: {
order: {
refundAmount: undefined,
refundType: undefined,
refundNote: undefined,
gmtRefund: undefined,
confirmTime: undefined,
gmtDelivery: undefined,
shipSn: undefined,
shipChannel: undefined,
gmtPay: undefined,
integralPrice: undefined,
payChannel: undefined,
skuPrice: undefined,
couponPrice: undefined,
freightPrice: undefined,
orderPrice: undefined,
},
member: {},
orderItems: [],
},
orderSourceMap,
orderStatusMap,
payTypeMap,
});
const {
loading,
queryParams,
orderList,
total,
dateRange,
} = toRefs(state);
function handleQuery() {
state.loading = true;
listOrderPages(state.queryParams).then(({ data }) => {
state.orderList = data.list;
state.total = data.total;
state.loading = false;
});
}
function resetQuery() {
queryFormRef.value.resetFields();
handleQuery();
}
function viewDetail(row: any) {
state.dialog.visible = true;
getOrderDetail(row.id).then((response: any) => {
state.orderDetail = response.data;
});
}
onMounted(() => {
handleQuery();
});
</script>
<template>
<div class="app-container">
<!-- 搜索表单 -->
@@ -116,118 +235,5 @@
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue";
import { ElForm } from "element-plus";
import { Dialog, Order, OrderQueryParam } from "@/types";
import { listOrderPages, getOrderDetail } from "@/api/oms/order";
import { Search, Refresh } from "@element-plus/icons-vue";
const queryFormRef = ref(ElForm);
const orderSourceMap = {
1: "微信小程序",
2: "APP",
3: "PC",
};
const orderStatusMap = {
101: "待付款",
102: "用户取消",
103: "系统取消",
201: "已付款",
202: "申请退款",
203: "已退款",
301: "待发货",
401: "已发货",
501: "用户收货",
502: "系统收货",
901: "已完成",
};
const payTypeMap = {
1: "支付宝",
2: "微信",
3: "会员余额",
};
const state = reactive({
loading: false,
ids: [],
single: true,
multiple: true,
dateRange: [],
queryParams: {
pageNum: 1,
pageSize: 10,
} as OrderQueryParam,
orderList: [] as Order[],
total: 0,
dialog: {
title: "订单详情",
visible: false,
} as Dialog,
dialogVisible: false,
orderDetail: {
order: {
refundAmount: undefined,
refundType: undefined,
refundNote: undefined,
gmtRefund: undefined,
confirmTime: undefined,
gmtDelivery: undefined,
shipSn: undefined,
shipChannel: undefined,
gmtPay: undefined,
integralPrice: undefined,
payChannel: undefined,
skuPrice: undefined,
couponPrice: undefined,
freightPrice: undefined,
orderPrice: undefined,
},
member: {},
orderItems: [],
},
orderSourceMap,
orderStatusMap,
payTypeMap,
});
const {
loading,
queryParams,
orderList,
total,
dateRange,
} = toRefs(state);
function handleQuery() {
state.loading = true;
listOrderPages(state.queryParams).then(({ data }) => {
state.orderList = data.list;
state.total = data.total;
state.loading = false;
});
}
function resetQuery() {
queryFormRef.value.resetFields();
handleQuery();
}
function viewDetail(row: any) {
state.dialog.visible = true;
getOrderDetail(row.id).then((response: any) => {
state.orderDetail = response.data;
});
}
onMounted(() => {
handleQuery();
});
</script>
<style scoped>
</style>

View File

@@ -1,76 +1,10 @@
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="danger" :icon="Delete" click="handleDelete" :disabled="multiple">删除</el-button>
</el-form-item>
<el-form-item prop="name">
<el-input v-model="queryParams.name" placeholder="品牌名称" />
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="brandList" @selection-change="handleSelectionChange" border>
<el-table-column type="selection" min-width="5%" />
<el-table-column prop="name" label="品牌名称" min-width="10" />
<el-table-column prop="logoUrl" label="LOGO" min-width="10">
<template #default="scope">
<el-popover placement="right" :width="400" trigger="hover">
<img :src="scope.row.logoUrl" width="400" height="400" />
<template #reference>
<img :src="scope.row.logoUrl" style="max-height: 60px; max-width: 60px" />
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" min-width="10" />
<el-table-column label="操作" width="150">
<template #default="scope">
<el-button @click="handleUpdate(scope.row)" type="primary" :icon="Edit" circle plain />
<el-button type="danger" :icon="Delete" circle plain @click="handleDelete(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页工具条 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="handleQuery" />
<!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" top="5vh" width="600px">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="品牌名称" prop="name">
<el-input v-model="formData.name" auto-complete="off" />
</el-form-item>
<el-form-item label="LOGO" prop="logoUrl">
<single-upload v-model="formData.logoUrl" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort" />
</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>
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "brand"
};
</script>
<script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue";
@@ -220,5 +154,79 @@ onMounted(() => {
});
</script>
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="danger" :icon="Delete" click="handleDelete" :disabled="multiple">删除</el-button>
</el-form-item>
<el-form-item prop="name">
<el-input v-model="queryParams.name" placeholder="品牌名称" />
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table v-loading="loading" :data="brandList" @selection-change="handleSelectionChange" border>
<el-table-column type="selection" min-width="5%" />
<el-table-column prop="name" label="品牌名称" min-width="10" />
<el-table-column prop="logoUrl" label="LOGO" min-width="10">
<template #default="scope">
<el-popover placement="right" :width="400" trigger="hover">
<img :src="scope.row.logoUrl" width="400" height="400" />
<template #reference>
<img :src="scope.row.logoUrl" style="max-height: 60px; max-width: 60px" />
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" min-width="10" />
<el-table-column label="操作" width="150">
<template #default="scope">
<el-button @click="handleUpdate(scope.row)" type="primary" :icon="Edit" circle plain />
<el-button type="danger" :icon="Delete" circle plain @click="handleDelete(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页工具条 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="handleQuery" />
<!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" top="5vh" width="600px">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="品牌名称" prop="name">
<el-input v-model="formData.name" auto-complete="off" />
</el-form-item>
<el-form-item label="LOGO" prop="logoUrl">
<single-upload v-model="formData.logoUrl" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort" />
</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 scoped>
</style>

View File

@@ -1,34 +1,11 @@
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="14" :xs="24">
<el-card class="box-card" shadow="always">
<template #header>
<svg-icon icon-class="menu"/>
商品分类
</template>
<Category ref="categoryRef" @categoryClick="handleCategoryClick"/>
</el-card>
</el-col>
<el-col :span="10" :xs="24">
<el-card class="box-card" shadow="always">
<template #header>
<svg-icon icon-class="menu"/>
{{category.name}} 规格属性
</template>
<!-- 商品规格 -->
<Attribute ref="specificationRef" :attributeType="1" :category="category"/>
<!-- 商品属性 -->
<Attribute ref="attributeRef" :attributeType="2" :category="category"/>
</el-card>
</el-col>
</el-row>
</div>
</template>
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "category"
};
</script>
<script setup lang="ts">
import Category from './components/Category.vue'
import Attribute from './components/Attribute.vue'
import SvgIcon from '@/components/SvgIcon/index.vue';
@@ -62,6 +39,35 @@ function handleCategoryClick(categoryRow: any) {
}
</script>
<template>
<div class="app-container">
<el-row :gutter="10">
<el-col :span="14" :xs="24">
<el-card class="box-card" shadow="always">
<template #header>
<svg-icon icon-class="menu"/>
商品分类
</template>
<Category ref="categoryRef" @categoryClick="handleCategoryClick"/>
</el-card>
</el-col>
<el-col :span="10" :xs="24">
<el-card class="box-card" shadow="always">
<template #header>
<svg-icon icon-class="menu"/>
{{category.name}} 规格属性
</template>
<!-- 商品规格 -->
<Attribute ref="specificationRef" :attributeType="1" :category="category"/>
<!-- 商品属性 -->
<Attribute ref="attributeRef" :attributeType="2" :category="category"/>
</el-card>
</el-col>
</el-row>
</div>
</template>
<style scoped>
</style>

View File

@@ -146,13 +146,13 @@ function handleNext() {
.filter((item) => item.main == true && item.url)
.map((item) => item.url);
if (mainPicUrl && mainPicUrl.length > 0) {
goodsInfo.picUrl = mainPicUrl[0];
goodsInfo.value.picUrl = mainPicUrl[0];
}
const subPicUrl = state.pictures
.filter((item) => item.main == false && item.url)
.map((item) => item.url);
if (subPicUrl && subPicUrl.length > 0) {
goodsInfo.subPicUrls = subPicUrl;
goodsInfo.value.subPicUrls = subPicUrl;
}
emit("next");
}

View File

@@ -203,7 +203,6 @@ watch(() => goodsInfo.value.categoryId, (newVal) => {
});
}
},
{
immediate: true,

View File

@@ -33,10 +33,10 @@ const state = reactive({
loaded: false,
active: 0,
goodsInfo: {
album: [] as string[],
attrList: [] as any[],
specList: [] as any[],
skuList: [] as any[]
album: [],
attrList: [],
specList: [],
skuList: []
} as GoodsDetail
});
@@ -48,8 +48,8 @@ function loadData() {
if (goodsId) {
getGoodsDetail(goodsId).then((response) => {
state.goodsInfo = response.data
state.goodsInfo.originPrice = state.goodsInfo.originPrice / 100
state.goodsInfo.price = state.goodsInfo.price / 100
state.goodsInfo.originPrice = (state.goodsInfo.originPrice as any) / 100
state.goodsInfo.price = (state.goodsInfo.price as any) / 100
state.loaded = true
})
} else {

View File

@@ -1,8 +1,129 @@
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "goods"
};
</script>
<script setup lang="ts">
import { reactive, ref, onMounted, toRefs } from "vue";
import { ElTable, ElMessage, ElMessageBox } from "element-plus";
import { useRouter } from "vue-router";
import { Search, Position, Edit, Refresh, Delete,View } from "@element-plus/icons-vue";
import { listGoodsPages, deleteGoods } from "@/api/pms/goods";
import { listCascadeCategories } from "@/api/pms/category";
import { GoodsItem, GoodsQueryParam } from "@/types";
import {moneyFormatter} from '@/utils/filter'
const dataTableRef = ref(ElTable);
const router = useRouter();
const state = reactive({
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10,
} as GoodsQueryParam,
goodsList: [] as GoodsItem[],
categoryOptions: [],
goodDetail: undefined,
dialogVisible: false,
});
const {
loading,
multiple,
queryParams,
goodsList,
categoryOptions,
goodDetail,
total,
dialogVisible,
} = toRefs(state);
function handleQuery() {
state.loading = true;
listGoodsPages(state.queryParams).then(({ data }) => {
state.goodsList = data.list;
state.total = data.total;
state.loading = false;
});
}
function resetQuery() {
state.queryParams = {
pageNum: 1,
pageSize: 10,
name: undefined,
categoryId: undefined,
};
handleQuery();
}
function handleGoodsView(detail: any) {
state.goodDetail = detail;
state.dialogVisible = true;
}
function handleAdd() {
router.push({ path: "goods-detail" });
}
function handleUpdate(row: any) {
router.push({
path: "goods-detail",
query: { goodsId: row.id, categoryId: row.categoryId },
});
}
function handleDelete(row: any) {
const ids = row.id || state.ids.join(",");
ElMessageBox.confirm("是否确认删除选中的数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(function () {
return deleteGoods(ids);
})
.then(() => {
ElMessage.success("删除成功");
handleQuery();
});
}
function handleRowClick(row: any) {
dataTableRef.value.toggleRowSelection(row);
}
function handleSelectionChange(selection: any) {
state.ids = selection.map((item: { id: any }) => item.id);
state.single = selection.length != 1;
state.multiple = !selection.length;
}
onMounted(() => {
listCascadeCategories({}).then((response) => {
state.categoryOptions = ref(response.data);
});
handleQuery();
});
</script>
<template>
<div class="app-container">
<el-form ref="queryForm" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd"
<el-button type="success" :icon="Position" @click="handleAdd"
>发布商品</el-button
>
<el-button
@@ -141,119 +262,5 @@
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted, toRefs } from "vue";
import { ElTable, ElMessage, ElMessageBox } from "element-plus";
import { useRouter } from "vue-router";
import { Search, Plus, Edit, Refresh, Delete,View } from "@element-plus/icons-vue";
import { listGoodsPages, deleteGoods } from "@/api/pms/goods";
import { listCascadeCategories } from "@/api/pms/category";
import { GoodsItem, GoodsQueryParam } from "@/types";
import {moneyFormatter} from '@/utils/filter'
const dataTableRef = ref(ElTable);
const router = useRouter();
const state = reactive({
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10,
} as GoodsQueryParam,
goodsList: [] as GoodsItem[],
categoryOptions: [],
goodDetail: undefined,
dialogVisible: false,
});
const {
loading,
multiple,
queryParams,
goodsList,
categoryOptions,
goodDetail,
total,
dialogVisible,
} = toRefs(state);
function handleQuery() {
state.loading = true;
listGoodsPages(state.queryParams).then(({ data }) => {
state.goodsList = data.list;
state.total = data.total;
state.loading = false;
});
}
function resetQuery() {
state.queryParams = {
pageNum: 1,
pageSize: 10,
name: undefined,
categoryId: undefined,
};
handleQuery();
}
function handleGoodsView(detail: any) {
state.goodDetail = detail;
state.dialogVisible = true;
}
function handleAdd() {
router.push({ path: "goods-detail" });
}
function handleUpdate(row: any) {
router.push({
path: "goods-detail",
query: { goodsId: row.id, categoryId: row.categoryId },
});
}
function handleDelete(row: any) {
const ids = row.id || state.ids.join(",");
ElMessageBox.confirm("是否确认删除选中的数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(function () {
return deleteGoods(ids);
})
.then(() => {
ElMessage.success("删除成功");
handleQuery();
});
}
function handleRowClick(row: any) {
dataTableRef.value.toggleRowSelection(row);
}
function handleSelectionChange(selection: any) {
state.ids = selection.map((item: { id: any }) => item.id);
state.single = selection.length != 1;
state.multiple = !selection.length;
}
onMounted(() => {
listCascadeCategories({}).then((response) => {
state.categoryOptions = ref(response.data);
});
handleQuery();
});
</script>
<style scoped>
</style>

View File

@@ -1,102 +1,9 @@
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-form-item>
<el-form-item prop="title">
<el-input v-model="queryParams.title" placeholder="广告标题" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="advertList" @selection-change="handleSelectionChange" border>
<el-table-column type="selection" min-width="5" align="center" />
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="title" min-width="100" label="广告标题" />
<el-table-column label="广告图片" width="100">
<template #default="scope">
<el-popover placement="right" :width="400" trigger="hover">
<img :src="scope.row.picUrl" width="400" height="400" />
<template #reference>
<img :src="scope.row.picUrl" style="max-height: 60px; max-width: 60px" />
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="beginTime" label="开始时间" width="150" />
<el-table-column prop="endTime" label="结束时间" width="150" />
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status === 1" type="success">开启</el-tag>
<el-tag v-else type="info">关闭</el-tag>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="80" />
<el-table-column label="操作" align="center" width="150">
<template #default="scope">
<el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
<el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页工具条 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="handleQuery" />
<!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="700px">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="广告标题" prop="title">
<el-input v-model="formData.title" />
</el-form-item>
<el-form-item label="有效期" prop="beginTime">
<el-date-picker v-model="formData.beginTime" placeholder="开始时间" value-format="YYYY-MM-DD" />
~
<el-date-picker v-model="formData.endTime" placeholder="结束时间" value-format="YYYY-MM-DD" />
</el-form-item>
<el-form-item label="广告图片" prop="picUrl">
<single-upload v-model="formData.picUrl" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort" style="width: 200px" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">开启</el-radio>
<el-radio :label="0">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="跳转链接" prop="url">
<el-input v-model="formData.url" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="formData.remark" />
</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>
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "advert"
};
</script>
<script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue";
@@ -235,3 +142,103 @@ onMounted(() => {
handleQuery();
});
</script>
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-form-item>
<el-form-item prop="title">
<el-input v-model="queryParams.title" placeholder="广告标题" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="advertList" @selection-change="handleSelectionChange" border>
<el-table-column type="selection" min-width="5" align="center" />
<el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="title" min-width="100" label="广告标题" />
<el-table-column label="广告图片" width="100">
<template #default="scope">
<el-popover placement="right" :width="400" trigger="hover">
<img :src="scope.row.picUrl" width="400" height="400" />
<template #reference>
<img :src="scope.row.picUrl" style="max-height: 60px; max-width: 60px" />
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="beginTime" label="开始时间" width="150" />
<el-table-column prop="endTime" label="结束时间" width="150" />
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status === 1" type="success">开启</el-tag>
<el-tag v-else type="info">关闭</el-tag>
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="80" />
<el-table-column label="操作" align="center" width="150">
<template #default="scope">
<el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
<el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页工具条 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="handleQuery" />
<!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="700px">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="广告标题" prop="title">
<el-input v-model="formData.title" />
</el-form-item>
<el-form-item label="有效期" prop="beginTime">
<el-date-picker v-model="formData.beginTime" placeholder="开始时间" value-format="YYYY-MM-DD" />
~
<el-date-picker v-model="formData.endTime" placeholder="结束时间" value-format="YYYY-MM-DD" />
</el-form-item>
<el-form-item label="广告图片" prop="picUrl">
<single-upload v-model="formData.picUrl" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort" style="width: 200px" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">开启</el-radio>
<el-radio :label="0">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="跳转链接" prop="url">
<el-input v-model="formData.url" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="formData.remark" />
</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>

View File

@@ -1,3 +1,174 @@
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "client"
};
</script>
<script setup lang="ts">
import {
listClientPages,
getClientFormDetial,
addClient,
updateClient,
deleteClients,
} from "@/api/system/client";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { onMounted, reactive, getCurrentInstance, ref, toRefs } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { ClientFormData, ClientItem, ClientQueryParam, Option } from "@/types";
const { proxy }: any = getCurrentInstance();
const queryFormRef = ref(ElForm);
const dataFormRef = ref(ElForm);
const state = reactive({
loading: true,
// 选中ID数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
queryParams: {
pageNum: 1,
pageSize: 10,
} as ClientQueryParam,
clientList: [] as ClientItem[],
total: 0,
dialog: {
title: "",
visible: false,
type: "create",
},
formData: {} as ClientFormData,
rules: {
clientId: [
{ required: true, message: "客户端ID不能为空", trigger: "blur" },
],
},
authorizedGrantTypesOptions: [] as Option[],
checkedAuthorizedGrantTypes: [] as string[]
});
const {
loading,
ids,
multiple,
queryParams,
clientList,
total,
dialog,
formData,
rules,
authorizedGrantTypesOptions,
checkedAuthorizedGrantTypes,
} = toRefs(state);
function handleQuery() {
listClientPages(state.queryParams).then(({ data }) => {
state.clientList = data.list;
state.total = data.total;
state.loading = false;
});
}
function resetQuery() {
queryFormRef.value.resetFields();
handleQuery();
}
function handleSelectionChange(selection: any) {
state.ids = selection.map((item: any) => item.clientId);
state.single = selection.length !== 1;
state.multiple = !selection.length;
}
function handleAdd() {
proxy.$listDictsByCode("grant_type").then((response: any) => {
state.authorizedGrantTypesOptions = response.data;
});
state.dialog = {
title: "添加客户端",
visible: true,
type: "create",
};
}
function handleUpdate(row: any) {
state.dialog = {
title: "修改客户端",
visible: true,
type: "edit",
};
const clientId = row.clientId || ids;
proxy.$listDictsByCode("grant_type").then((res: any) => {
state.authorizedGrantTypesOptions = res.data;
getClientFormDetial(clientId).then(({ data }) => {
state.formData = data;
state.checkedAuthorizedGrantTypes = data.authorizedGrantTypes?.split(",");
});
});
}
function submitForm() {
dataFormRef.value.validate((isvalid: boolean) => {
if (isvalid) {
state.formData.authorizedGrantTypes =
state.checkedAuthorizedGrantTypes.join(",");
if (state.dialog.type == "edit") {
updateClient(state.formData.clientId, state.formData).then(
() => {
ElMessage.success("修改成功");
state.dialog.visible = false;
cancel();
handleQuery();
}
);
} else {
addClient(state.formData).then(() => {
ElMessage.success("新增成功");
cancel();
handleQuery();
});
}
}
});
}
function resetForm() {
dataFormRef.value.resetFields();
state.checkedAuthorizedGrantTypes = [];
}
function cancel() {
resetForm();
state.dialog.visible = false;
}
function handleDelete(row: any) {
const clientIds = [row.clientId || ids].join(",");
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
deleteClients(clientIds).then(() => {
ElMessage.success("删除成功");
handleQuery();
});
})
.catch(() => ElMessage.info("已取消删除"));
}
onMounted(() => {
handleQuery();
});
</script>
<template>
<div class="app-container">
<!-- 搜索表单 -->
@@ -126,168 +297,4 @@
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import {
listClientPages,
getClientFormDetial,
addClient,
updateClient,
deleteClients,
} from "@/api/system/client";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { onMounted, reactive, getCurrentInstance, ref, toRefs } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { ClientFormData, ClientItem, ClientQueryParam, Option } from "@/types";
const { proxy }: any = getCurrentInstance();
const queryFormRef = ref(ElForm);
const dataFormRef = ref(ElForm);
const state = reactive({
loading: true,
// 选中ID数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
queryParams: {
pageNum: 1,
pageSize: 10,
} as ClientQueryParam,
clientList: [] as ClientItem[],
total: 0,
dialog: {
title: "",
visible: false,
type: "create",
},
formData: {} as ClientFormData,
rules: {
clientId: [
{ required: true, message: "客户端ID不能为空", trigger: "blur" },
],
},
authorizedGrantTypesOptions: [] as Option[],
checkedAuthorizedGrantTypes: [] as string[],
});
const {
loading,
ids,
multiple,
queryParams,
clientList,
total,
dialog,
formData,
rules,
authorizedGrantTypesOptions,
checkedAuthorizedGrantTypes,
} = toRefs(state);
function handleQuery() {
listClientPages(state.queryParams).then(({ data }) => {
state.clientList = data.list;
state.total = data.total;
state.loading = false;
});
}
function resetQuery() {
queryFormRef.value.resetFields();
handleQuery();
}
function handleSelectionChange(selection: any) {
state.ids = selection.map((item: any) => item.clientId);
state.single = selection.length !== 1;
state.multiple = !selection.length;
}
function handleAdd() {
proxy.$listDictsByCode("grant_type").then((response: any) => {
state.authorizedGrantTypesOptions = response.data;
});
state.dialog = {
title: "添加客户端",
visible: true,
type: "create",
};
}
function handleUpdate(row: any) {
state.dialog = {
title: "修改客户端",
visible: true,
type: "edit",
};
const clientId = row.clientId || ids;
proxy.$listDictsByCode("grant_type").then((res: any) => {
state.authorizedGrantTypesOptions = res.data;
getClientFormDetial(clientId).then(({ data }) => {
state.formData = data;
state.checkedAuthorizedGrantTypes = data.authorizedGrantTypes?.split(",");
});
});
}
function submitForm() {
dataFormRef.value.validate((isvalid: boolean) => {
if (isvalid) {
state.formData.authorizedGrantTypes =
state.checkedAuthorizedGrantTypes.join(",");
if (state.dialog.type == "edit") {
updateClient(state.formData.clientId, state.formData).then(
() => {
ElMessage.success("修改成功");
state.dialog.visible = false;
cancel();
handleQuery();
}
);
} else {
addClient(state.formData).then(() => {
ElMessage.success("新增成功");
cancel();
handleQuery();
});
}
}
});
}
function resetForm() {
dataFormRef.value.resetFields();
state.checkedAuthorizedGrantTypes = [];
}
function cancel() {
resetForm();
state.dialog.visible = false;
}
function handleDelete(row: any) {
const clientIds = [row.clientId || ids].join(",");
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
deleteClients(clientIds).then(() => {
ElMessage.success("删除成功");
handleQuery();
});
})
.catch(() => ElMessage.info("已取消删除"));
}
onMounted(() => {
handleQuery();
});
</script>
</template>

View File

@@ -1,85 +1,9 @@
<template>
<div class="app-container">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="danger" :icon="Delete" :disabled="single" @click="handleDelete">删除
</el-button>
</el-form-item>
<el-form-item prop="name">
<el-input v-model="queryParams.name" placeholder="请输入部门名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item prop="status">
<el-select v-model="queryParams.status" placeholder="部门状态" clearable>
<el-option :value="1" label="正常" />
<el-option :value="0" label="禁用" />
</el-select>
</el-form-item>
<el-form-item>
<el-button class="filter-item" type="primary" :icon="Search" @click="handleQuery">
搜索
</el-button>
<el-button :icon="Refresh" @click="resetQuery"> 重置 </el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="deptList" row-key="id" default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="name" label="部门名称" />
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status == 1" type="success">正常</el-tag>
<el-tag v-else type="info">禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="sort" label="显示排序" width="200" />
<el-table-column label="操作" align="center" width="150">
<template #default="scope">
<el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)">
</el-button>
<el-button type="success" :icon="Plus" circle plain @click.stop="handleAdd(scope.row)">
</el-button>
<el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)">
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改部门对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" @closed="cancel">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="80px">
<el-form-item label="上级部门" prop="parentId">
<el-tree-select v-model="formData.parentId" placeholder="选择上级部门" :data="deptOptions" filterable check-strictly />
</el-form-item>
<el-form-item label="部门名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入部门名称" />
</el-form-item>
<el-form-item label="显示排序" prop="sort">
<el-input-number v-model="formData.sort" controls-position="right" style="width: 100px" :min="0" />
</el-form-item>
<el-form-item label="部门状态">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</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>
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "dept"
};
</script>
<script setup lang="ts">
// Vue依赖
@@ -92,7 +16,7 @@ import {
updateDept,
addDept,
listSelectDepartments,
listTableDepartments,
listTableDepartments
} from "@/api/system/dept";
// 组件依赖
@@ -195,7 +119,7 @@ async function loadDeptData() {
*/
function handleAdd(row: any) {
loadDeptData();
state.formData.id=undefined
state.formData.id = undefined
state.formData.parentId = row.id;
state.dialog = {
title: "添加部门",
@@ -278,3 +202,85 @@ onMounted(() => {
handleQuery();
});
</script>
<template>
<div class="app-container">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="danger" :icon="Delete" :disabled="single" @click="handleDelete">删除
</el-button>
</el-form-item>
<el-form-item prop="name">
<el-input v-model="queryParams.name" placeholder="请输入部门名称" @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item prop="status">
<el-select v-model="queryParams.status" placeholder="部门状态" clearable>
<el-option :value="1" label="正常" />
<el-option :value="0" label="禁用" />
</el-select>
</el-form-item>
<el-form-item>
<el-button class="filter-item" type="primary" :icon="Search" @click="handleQuery">
搜索
</el-button>
<el-button :icon="Refresh" @click="resetQuery"> 重置 </el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="deptList" row-key="id" default-expand-all
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column prop="name" label="部门名称" />
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag v-if="scope.row.status == 1" type="success">正常</el-tag>
<el-tag v-else type="info">禁用</el-tag>
</template>
</el-table-column>
<el-table-column prop="sort" label="显示排序" width="200" />
<el-table-column label="操作" align="center" width="150">
<template #default="scope">
<el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)">
</el-button>
<el-button type="success" :icon="Plus" circle plain @click.stop="handleAdd(scope.row)">
</el-button>
<el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)">
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 添加或修改部门对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" @closed="cancel">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="80px">
<el-form-item label="上级部门" prop="parentId">
<el-tree-select v-model="formData.parentId" placeholder="选择上级部门" :data="deptOptions" filterable check-strictly />
</el-form-item>
<el-form-item label="部门名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入部门名称" />
</el-form-item>
<el-form-item label="显示排序" prop="sort">
<el-input-number v-model="formData.sort" controls-position="right" style="width: 100px" :min="0" />
</el-form-item>
<el-form-item label="部门状态">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</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>

View File

@@ -1,3 +1,10 @@
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "dict"
};
</script>
<template>
<div class="app-container">
<!-- 搜索表单 -->

View File

@@ -57,26 +57,30 @@
<el-input v-model="formData.name" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="是否外链">
<el-radio-group v-model="isExternalPath">
<el-radio :label="false"></el-radio>
<el-radio :label="true"></el-radio>
<el-form-item label="菜单类型">
<el-radio-group v-model="formData.type" @change="handleMenuTypeChange">
<el-radio label="MENU">菜单</el-radio>
<el-radio label="CATALOG">目录</el-radio>
<el-radio label="EXTLINK">外链</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="isExternalPath" label="外链地址" prop="path">
<!-- 路由路径(浏览器地址栏显示) -->
<el-form-item v-if="formData.type == 'EXTLINK'" label="外链地址" prop="path">
<el-input v-model="formData.path" placeholder="请输入外链完整路径" />
</el-form-item>
<el-form-item v-if="!isExternalPath" label="页面路径" prop="component">
<el-form-item v-else label="路由路径" prop="path">
<el-input v-if="formData.type == 'CATALOG'" v-model="formData.path" placeholder="/system (注意:目录以/开头)" />
<el-input v-else v-model="formData.path" placeholder="user" />
</el-form-item>
<!-- 组件页面完整路径 -->
<el-form-item v-if="formData.type == 'MENU'" label="组件路径" prop="component">
<el-input v-model="formData.component" placeholder="system/user/index" style="width: 95%">
<template v-if="formData.parentId != '0'" #prepend>src/views/</template>
<template v-if="formData.parentId != '0'" #append>.vue</template>
</el-input>
<el-tooltip effect="dark" content="请输入组件路径,如果是父组件填写 Layout 即可" placement="right">
<i class="el-icon-info" style="margin-left: 10px; color: darkseagreen"></i>
</el-tooltip>
</el-form-item>
<el-form-item label="图标" prop="icon">
@@ -93,6 +97,10 @@
</el-popover>
</el-form-item>
<el-form-item label="跳转路由">
<el-input v-model="formData.redirect" placeholder="跳转路由路径" maxlength="50" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="formData.visible">
<el-radio :label="1">显示</el-radio>
@@ -104,9 +112,7 @@
<el-input-number v-model="formData.sort" style="width: 100px" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="跳转路径">
<el-input v-model="formData.redirect" placeholder="请输入跳转路径" maxlength="50" />
</el-form-item>
</el-form>
<template #footer>
@@ -125,7 +131,6 @@ import { reactive, ref, onMounted, toRefs } from "vue";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { ElForm, ElMessage, ElMessageBox, ElPopover } from "element-plus";
import { isExternal } from "@/utils/validate";
import {
Dialog,
Option,
@@ -164,20 +169,28 @@ const state = reactive({
dialog: { visible: false } as Dialog,
formData: {
parentId: "0",
name: '',
visible: 1,
sort: 1,
component: "Layout",
component: 'Layout',
type: 'MENU'
} as MenuFormData,
rules: {
parentId: [{ required: true, message: "请选择顶级菜单", trigger: "blur" }],
name: [{ required: true, message: "请输入菜单名称", trigger: "blur" }],
component: [{ required: true, message: "请输入页面路径", trigger: "blur" }],
type: [{ required: true, message: "请选择菜单类型", trigger: "blur" }],
path: [{ required: true, message: "请输入路由路径", trigger: "blur" }],
component: [{ required: true, message: "请输入组件完整路径", trigger: "blur" }]
},
menuOptions: [] as Option[],
currentRow: undefined,
isExternalPath: false,
// Icon选择器显示状态
iconSelectVisible: false
iconSelectVisible: false,
cacheData: {
menuType: '',
menuPath: ''
}
});
const {
@@ -188,8 +201,8 @@ const {
formData,
rules,
menuOptions,
isExternalPath,
iconSelectVisible
iconSelectVisible,
cacheData
} = toRefs(state);
/**
@@ -237,26 +250,29 @@ async function handleAdd(row: any) {
title: "添加菜单",
visible: true
};
if (row.id) {
// 行点击新增
if (row.id) { // 行点击新增
state.formData.parentId = row.id;
if (row.id == '0') {
state.formData.component = "Layout";
state.formData.type = 'CATALOG';
} else {
state.formData.component = undefined;
state.formData.type = 'MENU';
}
} else {
} else { // 工具栏新增
if (state.currentRow) {
// 工具栏新增
state.formData.parentId = (state.currentRow as any).id;
state.formData.component = undefined;
state.formData.type = 'MENU';
} else {
state.formData.parentId = "0";
state.formData.component = "Layout";
state.formData.type = 'CATALOG';
}
}
}
/**
* 修改弹窗
*/
async function handleUpdate(row: any) {
await loadMenuData();
state.dialog = {
@@ -266,13 +282,25 @@ async function handleUpdate(row: any) {
const id = row.id || state.ids;
getMenuDetail(id).then(({ data }) => {
state.formData = data;
// 判断是否外部链接
state.isExternalPath = isExternal(state.formData.path);
cacheData.value.menuType = data.type
cacheData.value.menuPath = data.path
});
}
/**
* 菜单表单提交
* 菜单类型change事件
*/
function handleMenuTypeChange(val: any) {
if (val !== cacheData.value.menuType) {
formData.value.path = ''
} else {
formData.value.path = cacheData.value.menuPath
}
}
/**
* 菜单提交
*/
function submitForm() {
dataFormRef.value.validate((isValid: boolean) => {
@@ -326,7 +354,6 @@ function selected(name: string) {
state.iconSelectVisible = false;
}
onMounted(() => {
handleQuery();
});

View File

@@ -1,33 +1,23 @@
<template>
<div class="menu-container">
<el-row>
<el-col :span="12">
<el-button plain :icon="Switch" @click="toggleExpandAll">展开/折叠</el-button>
</el-col>
<el-col :span="12" style="text-align: right">
<el-button type="primary" :icon="Check" @click="handleSubmit">提交</el-button>
</el-col>
</el-row>
<el-tree class="menu-container__tree" ref="menuRef" v-if="refreshTree" :default-expanded-keys="expandedKeys" :default-expand-all="isExpandAll"
:data="menuOptions" show-checkbox node-key="value" empty-text="加载菜单中..." :check-strictly="checkStrictly"
highlight-current @node-click="handleNodeClick" />
</div>
</template>
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "cmenu"
};
</script>
<script setup lang="ts">
import { nextTick, onMounted, reactive, ref, toRefs, watch } from "vue";
import { listSelectMenus } from "@/api/system/menu";
import { listRoleMenuIds, updateRoleMenu } from "@/api/system/role";
import { ElTree, ElMessage } from "element-plus";
import { Switch, Check } from "@element-plus/icons-vue";
import { Switch, Position } from "@element-plus/icons-vue";
import { Option } from "@/types";
const emit = defineEmits(["menuClick"]);
const props = defineProps({
role: {
type: Object,
default: () => { }
default: () => {}
},
});
@@ -99,10 +89,25 @@ onMounted(() => {
});
</script>
<template>
<div class="app-container">
<el-row>
<el-col :span="12">
<el-button plain :icon="Switch" @click="toggleExpandAll">展开/折叠</el-button>
</el-col>
<el-col :span="12" style="text-align: right">
<el-button type="primary" :icon="Position" @click="handleSubmit">提交</el-button>
</el-col>
</el-row>
<el-tree class="menu-tree" ref="menuRef" v-if="refreshTree" :default-expanded-keys="expandedKeys"
:default-expand-all="isExpandAll" :data="menuOptions" show-checkbox node-key="value" empty-text="加载菜单中..."
:check-strictly="checkStrictly" highlight-current @node-click="handleNodeClick" />
</div>
</template>
<style lang="scss" scoped>
.menu-container {
&__tree{
margin-top: 10px;
}
.menu-tree {
margin-top: 10px;
}
</style>

View File

@@ -7,7 +7,7 @@
</el-checkbox>
</el-col>
<el-col :span="12" style="text-align: right">
<el-button type="primary" :icon="Check" @click="handleSubmit">提交</el-button>
<el-button type="primary" :icon="Position" @click="handleSubmit">提交</el-button>
</el-col>
</el-row>
@@ -36,7 +36,7 @@ import { onMounted, reactive, toRefs, watch } from "vue";
import { listPerms } from "@/api/system/perm";
import { listRolePerms, saveRolePerms } from "@/api/system/role";
import { ElMessage } from "element-plus";
import { Check } from "@element-plus/icons-vue";
import { Position } from "@element-plus/icons-vue";
import { PermQueryParam } from "@/types";
const props = defineProps({

View File

@@ -1,72 +1,9 @@
<template>
<div class="role-container">
<!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-form-item>
<el-form-item prop="name">
<el-input v-model="queryParams.name" placeholder="角色名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table ref="dataTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"
@row-click="handleRowClick" highlight-current-row border>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="角色名称" prop="name" />
<el-table-column label="角色编码" prop="code" />
<el-table-column label="操作" align="center" width="120">
<template #default="scope">
<el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
<el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页工具条 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
@pagination="handleQuery" />
<!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" @close="cancel" width="450px">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="角色名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入角色名称" />
</el-form-item>
<el-form-item label="角色编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入角色编码" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="formData.sort" controls-position="right" :min="0" style="width: 100px" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</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>
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "role"
};
</script>
<script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue";
@@ -107,7 +44,7 @@ const state = reactive({
rules: {
name: [{ required: true, message: "请输入角色名称", trigger: "blur" }],
code: [{ required: true, message: "请输入角色编码", trigger: "blur" }],
},
}
});
const {
@@ -215,8 +152,77 @@ onMounted(() => {
});
</script>
<template>
<div class="app-container">
<!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
<el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
</el-form-item>
<el-form-item prop="name">
<el-input v-model="queryParams.name" placeholder="角色名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 数据表格 -->
<el-table ref="dataTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"
@row-click="handleRowClick" highlight-current-row border>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="角色名称" prop="name" />
<el-table-column label="角色编码" prop="code" />
<el-table-column label="操作" align="center" width="120">
<template #default="scope">
<el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
<el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页工具条 -->
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="handleQuery" />
<!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" @close="cancel" width="450px">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
<el-form-item label="角色名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入角色名称" />
</el-form-item>
<el-form-item label="角色编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入角色编码" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="formData.sort" controls-position="right" :min="0" style="width: 100px" />
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</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 lang="scss" scoped>
.role-container {
width: 100%;
}
</style>

View File

@@ -1,189 +1,9 @@
<template>
<div class="app-container">
<el-row :gutter="20">
<!-- 部门树 -->
<el-col :span="4" :xs="24">
<el-card class="box-card">
<el-input v-model="deptName" placeholder="部门名称" clearable :prefix-icon="Search" style="margin-bottom: 20px" />
<el-tree ref="deptTreeRef" :data="deptOptions" :props="{ children: 'children', label: 'label', disabled: '' }"
:expand-on-click-node="false" :filter-node-method="filterDeptNode" default-expand-all
@node-click="handleDeptNodeClick"></el-tree>
</el-card>
</el-col>
<!-- 用户数据 -->
<el-col :span="20" :xs="24">
<el-card class="box-card">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-row>
<el-col :span="18" :xs="24" >
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" v-hasPerm="['sys:user:add']">新增</el-button>
<el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete"
v-hasPerm="['sys:user:delete']">删除</el-button>
</el-form-item>
<el-form-item prop="keywords">
<el-input v-model="queryParams.keywords" placeholder="用户名/昵称/手机号" clearable style="width: 200px"
@keyup.enter="handleQuery" />
</el-form-item>
<el-form-item prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 200px">
<el-option label="正常" value="1" />
<el-option label="停用" value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24" style="text-align: right;">
<el-form-item>
<el-dropdown split-button style="margin-left: 12px;">
导入
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :icon="Download" @click="handleDownloadTemplate">下载模板</el-dropdown-item>
<el-dropdown-item :icon="Top" @click="showImportDialog">导入数据</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button :icon="Download" style="margin-left: 12px;" @click="handleExport">导出</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column key="id" label="用户编号" align="center" prop="id" />
<el-table-column key="username" label="用户名" align="center" prop="username" />
<el-table-column label="用户昵称" align="center" prop="nickname" />
<el-table-column label="性别" align="center" prop="gender" />
<el-table-column label="部门" align="center" prop="deptName" />
<el-table-column label="手机号码" align="center" prop="mobile" width="120" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-switch v-model="scope.row.status" :inactive-value="0" :active-value="1"
@change="handleStatusChange(scope.row)" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="gmtCreate" width="180"></el-table-column>
<el-table-column label="操作" align="center" width="150">
<template #default="scope">
<el-button type="primary" :icon="Edit" circle plain @click="handleUpdate(scope.row)"
v-hasPerm="['sys:user:edit']"></el-button>
<el-button type="danger" :icon="Delete" circle plain @click="handleDelete(scope.row)"
v-hasPerm="['sys:user:delete']"></el-button>
<el-button type="warning" :icon="Lock" circle plain @click="resetPassword(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="handleQuery" />
</el-card>
</el-col>
</el-row>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="cancel">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input :readonly="!!formData.id" v-model="formData.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="用户昵称" prop="nickname">
<el-input v-model="formData.nickname" placeholder="请输入用户昵称" />
</el-form-item>
<el-form-item label="所属部门" prop="deptId">
<el-tree-select v-model="formData.deptId" placeholder="请选择所属部门" :data="deptOptions" filterable />
</el-form-item>
<el-form-item label="手机号码" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户性别" prop="gender">
<el-select v-model="formData.gender" placeholder="请选择">
<el-option label="未知" :value="0" />
<el-option label="男" :value="1" />
<el-option label="女" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="角色" prop="roleIds">
<el-select v-model="formData.roleIds" multiple placeholder="请选择">
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</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>
<el-dialog :title="importDialog.title" v-model="importDialog.visible" width="600px" append-to-body
@close="closeImportDialog">
<el-form ref="importFormRef" :model="importFormData" :rules="rules" label-width="80px">
<el-form-item label="所属部门" prop="deptId">
<el-tree-select v-model="formData.deptId" placeholder="请选择所属部门" :data="deptOptions" filterable />
</el-form-item>
<el-form-item label="角色" prop="roleIds">
<el-select v-model="importFormData.roleIds" multiple placeholder="请选择">
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="Excel">
<el-upload class="upload-demo" action="" drag :auto-upload="false"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
:file-list="excelFilelist" :on-change="handleExcelChange" :limit="1">
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">xls/xlsx files </div>
</template>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitImportForm"> </el-button>
<el-button @click="closeImportDialog"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "user"
};
</script>
<script setup lang='ts'>
// Vue依赖
@@ -348,10 +168,10 @@ function filterDeptNode(value: string, data: any) {
}
/**
* 部门树节点点击
* 部门树节点点击事件
*/
function handleDeptNodeClick(data: { [key: string]: any }) {
state.queryParams.deptId = data.id;
state.queryParams.deptId = data.value;
handleQuery();
}
@@ -572,7 +392,7 @@ async function showImportDialog() {
state.importDialog.visible = true
}
// Excel文件上传
function handleExcelChange(file: UploadFile) {
if (!/\.(xlsx|xls|XLSX|XLS)$/.test(file.name)) {
ElMessage.warning('上传Excel只能为xlsx、xls格式');
@@ -645,6 +465,192 @@ onMounted(() => {
loadData();
});
</script>
<template>
<div class="app-container">
<el-row :gutter="20">
<!-- 部门树 -->
<el-col :span="4" :xs="24">
<el-card class="box-card">
<el-input v-model="deptName" placeholder="部门名称" clearable :prefix-icon="Search" style="margin-bottom: 20px" />
<el-tree ref="deptTreeRef" :data="deptOptions" :props="{ children: 'children', label: 'label', disabled: '' }"
:expand-on-click-node="false" :filter-node-method="filterDeptNode" default-expand-all
@node-click="handleDeptNodeClick"></el-tree>
</el-card>
</el-col>
<!-- 用户数据 -->
<el-col :span="20" :xs="24">
<el-card class="box-card">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-row>
<el-col :span="18" :xs="24" >
<el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" v-hasPerm="['sys:user:add']">新增</el-button>
<el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete"
v-hasPerm="['sys:user:delete']">删除</el-button>
</el-form-item>
<el-form-item prop="keywords">
<el-input v-model="queryParams.keywords" placeholder="用户名/昵称/手机号" clearable style="width: 200px"
@keyup.enter="handleQuery" />
</el-form-item>
<el-form-item prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 200px">
<el-option label="正常" value="1" />
<el-option label="停用" value="0" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24" style="text-align: right;">
<el-form-item>
<el-dropdown split-button style="margin-left: 12px;">
导入
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :icon="Download" @click="handleDownloadTemplate">下载模板</el-dropdown-item>
<el-dropdown-item :icon="Top" @click="showImportDialog">导入数据</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button :icon="Download" style="margin-left: 12px;" @click="handleExport">导出</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column key="id" label="用户编号" align="center" prop="id" />
<el-table-column key="username" label="用户名" align="center" prop="username" />
<el-table-column label="用户昵称" align="center" prop="nickname" />
<el-table-column label="性别" align="center" prop="gender" />
<el-table-column label="部门" align="center" prop="deptName" />
<el-table-column label="手机号码" align="center" prop="mobile" width="120" />
<el-table-column label="状态" align="center" prop="status">
<template #default="scope">
<el-switch v-model="scope.row.status" :inactive-value="0" :active-value="1"
@change="handleStatusChange(scope.row)" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="gmtCreate" width="180"></el-table-column>
<el-table-column label="操作" align="center" width="150">
<template #default="scope">
<el-button type="primary" :icon="Edit" circle plain @click="handleUpdate(scope.row)"
v-hasPerm="['sys:user:edit']"></el-button>
<el-button type="danger" :icon="Delete" circle plain @click="handleDelete(scope.row)"
v-hasPerm="['sys:user:delete']"></el-button>
<el-button type="warning" :icon="Lock" circle plain @click="resetPassword(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" @pagination="handleQuery" />
</el-card>
</el-col>
</el-row>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="cancel">
<el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="80px">
<el-form-item label="用户名" prop="username">
<el-input :readonly="!!formData.id" v-model="formData.username" placeholder="请输入用户名" />
</el-form-item>
<el-form-item label="用户昵称" prop="nickname">
<el-input v-model="formData.nickname" placeholder="请输入用户昵称" />
</el-form-item>
<el-form-item label="所属部门" prop="deptId">
<el-tree-select v-model="formData.deptId" placeholder="请选择所属部门" :data="deptOptions" filterable check-strictly />
</el-form-item>
<el-form-item label="手机号码" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机号码" maxlength="11" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="formData.email" placeholder="请输入邮箱" maxlength="50" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio>
<el-radio :label="0">禁用</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户性别" prop="gender">
<el-select v-model="formData.gender" placeholder="请选择">
<el-option label="未知" :value="0" />
<el-option label="男" :value="1" />
<el-option label="女" :value="2" />
</el-select>
</el-form-item>
<el-form-item label="角色" prop="roleIds">
<el-select v-model="formData.roleIds" multiple placeholder="请选择">
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</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>
<el-dialog :title="importDialog.title" v-model="importDialog.visible" width="600px" append-to-body
@close="closeImportDialog">
<el-form ref="importFormRef" :model="importFormData" :rules="rules" label-width="80px">
<el-form-item label="部门" prop="deptId">
<el-tree-select v-model="formData.deptId" placeholder="请选择部门" :data="deptOptions" filterable check-strictly />
</el-form-item>
<el-form-item label="角色" prop="roleIds">
<el-select v-model="importFormData.roleIds" multiple placeholder="请选择">
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="Excel">
<el-upload class="upload-demo" action="" drag :auto-upload="false"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
:file-list="excelFilelist" :on-change="handleExcelChange" :limit="1">
<el-icon class="el-icon--upload">
<upload-filled />
</el-icon>
<div class="el-upload__text">
将文件拖到此处
<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">xls/xlsx files </div>
</template>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitImportForm"> </el-button>
<el-button @click="closeImportDialog"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style lang="scss" scoped>
</style>

View File

@@ -1,3 +1,67 @@
<!-- setup 无法设置组件名称组件名称keepAlive必须 -->
<script lang="ts">
export default {
name: "member"
};
</script>
<script setup lang="ts">
import { reactive, onMounted, toRefs } from "vue";
import { ElTable } from "element-plus";
import { Search, Refresh } from "@element-plus/icons-vue";
import { listMemebersPage } from "@/api/ums/member";
import { MemberQueryParam, MemberItem } from "@/types";
const state = reactive({
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10
} as MemberQueryParam,
memberList: [] as MemberItem[]
});
const { loading, queryParams, memberList, total } =
toRefs(state);
function handleQuery() {
state.loading = true;
listMemebersPage(state.queryParams).then(({ data }) => {
state.memberList = data.list;
state.total = data.total;
state.loading = false;
});
}
function resetQuery() {
state.queryParams = {
pageNum: 1,
pageSize: 10,
nickName: ''
};
handleQuery();
}
function handleSelectionChange(selection: any) {
state.ids = selection.map((item: { id: any }) => item.id);
state.single = selection.length != 1;
state.multiple = !selection.length;
}
onMounted(() => {
handleQuery();
});
</script>
<template>
<div class="app-container">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
@@ -81,62 +145,5 @@
</div>
</template>
<script setup lang="ts">
import { reactive, onMounted, toRefs } from "vue";
import { ElTable } from "element-plus";
import { Search, Refresh } from "@element-plus/icons-vue";
import { listMemeberPages } from "@/api/ums/member";
import { MemberQueryParam, MemberItem } from "@/types";
const state = reactive({
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10
} as MemberQueryParam,
memberList: [] as MemberItem[]
});
const { loading, queryParams, memberList, total } =
toRefs(state);
function handleQuery() {
state.loading = true;
listMemeberPages(state.queryParams).then(({ data }) => {
state.memberList = data.list;
state.total = data.total;
state.loading = false;
});
}
function resetQuery() {
state.queryParams = {
pageNum: 1,
pageSize: 10,
nickName: ''
};
handleQuery();
}
function handleSelectionChange(selection: any) {
state.ids = selection.map((item: { id: any }) => item.id);
state.single = selection.length != 1;
state.multiple = !selection.length;
}
onMounted(() => {
handleQuery();
});
</script>
<style scoped>
</style>