Merge branch 'master' of https://e.coding.net/g-cphe0354/cashier/cashier-web into dwb
This commit is contained in:
@@ -13,7 +13,7 @@ VITE_APP_API_URL=https://tapi.cashier.sxczgkj.cn/ # 正式
|
||||
# VITE_APP_API_URL=http://localhost:8989 # 本地
|
||||
|
||||
# WebSocket 端点(不配置则关闭),线上 ws://api.youlai.tech/ws ,本地 ws://localhost:8989/ws
|
||||
VITE_APP_WS_ENDPOINT=
|
||||
VITE_APP_WS_ENDPOINT=wss://sockets.sxczgkj.com/wss
|
||||
|
||||
# 启用 Mock 服务
|
||||
VITE_MOCK_DEV_SERVER=false
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
订单,支付相关:<https://tapi.cashier.sxczgkj.cn/order/>
|
||||
商品,耗材相关:<https://tapi.cashier.sxczgkj.cn/product/>
|
||||
系统相关:<https://tapi.cashier.sxczgkj.cn/system/>
|
||||
购物车websocket
|
||||
<https://apifox.com/apidoc/shared-2c47d665-b476-409c-9cb5-0e68a69b0292>
|
||||
|
||||
## 项目特色
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"path-to-regexp": "^8.2.0",
|
||||
"pinia": "^2.3.1",
|
||||
"qs": "^6.14.0",
|
||||
"sockjs-client": "^1.6.1",
|
||||
"sortablejs": "^1.15.6",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.1.0",
|
||||
|
||||
85
src/api/account/activate.ts
Normal file
85
src/api/account/activate.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import request from "@/utils/request";
|
||||
import { Account_BaseUrl } from "@/api/config";
|
||||
const baseURL = Account_BaseUrl + "/admin/activate";
|
||||
const API = {
|
||||
|
||||
getList() {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "get",
|
||||
});
|
||||
},
|
||||
edit(data: editRequest) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
add(data: addRequest) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
export default API;
|
||||
|
||||
export interface addRequest {
|
||||
/**
|
||||
* 充值金额
|
||||
*/
|
||||
amount?: number;
|
||||
/**
|
||||
* 优惠卷id
|
||||
*/
|
||||
couponId?: number;
|
||||
/**
|
||||
* 赠送金额
|
||||
*/
|
||||
giftAmount?: number;
|
||||
/**
|
||||
* 赠送积分
|
||||
*/
|
||||
giftPoints?: number;
|
||||
/**
|
||||
* 是否赠送优惠卷 0否 1是
|
||||
*/
|
||||
isGiftCoupon?: number;
|
||||
/**
|
||||
* 优惠卷数量
|
||||
*/
|
||||
num?: number;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
export interface editRequest {
|
||||
/**
|
||||
* 充值金额
|
||||
*/
|
||||
amount: number;
|
||||
/**
|
||||
* 优惠卷id
|
||||
*/
|
||||
couponId?: number;
|
||||
/**
|
||||
* 赠送金额
|
||||
*/
|
||||
giftAmount?: number;
|
||||
/**
|
||||
* 赠送积分
|
||||
*/
|
||||
giftPoints?: number;
|
||||
id: number;
|
||||
/**
|
||||
* 是否赠送优惠卷 0否 1是
|
||||
*/
|
||||
isGiftCoupon?: number;
|
||||
/**
|
||||
* 优惠卷数量
|
||||
*/
|
||||
num?: number;
|
||||
shopId?: number;
|
||||
[property: string]: any;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ const MenuApi = {
|
||||
return request<any, MenuVO>({
|
||||
url: `${baseURL}/detail`,
|
||||
method: "get",
|
||||
data: { id }
|
||||
params: { id }
|
||||
});
|
||||
},
|
||||
add(data: addRequest) {
|
||||
@@ -195,7 +195,7 @@ export interface editRequest {
|
||||
/**
|
||||
* 上级菜单,不传递则为顶级菜单
|
||||
*/
|
||||
pid?: number;
|
||||
pid: number | string | null;
|
||||
title: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
22
src/api/account/permission.ts
Normal file
22
src/api/account/permission.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import request from "@/utils/request";
|
||||
import { Account_BaseUrl } from "@/api/config";
|
||||
const baseURL = Account_BaseUrl + "/admin";
|
||||
const ShopStaffApi = {
|
||||
// 获取店铺权限列表
|
||||
getshopPermission() {
|
||||
return request<any>({
|
||||
url: `${baseURL}/shopPermission`,
|
||||
method: "get",
|
||||
});
|
||||
},
|
||||
// 获取员工对应的权限id
|
||||
getPermission(id: number | string) {
|
||||
return request<any>({
|
||||
url: `${baseURL}/shopStaff/permission`,
|
||||
method: "get",
|
||||
params: { id }
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default ShopStaffApi;
|
||||
@@ -17,20 +17,29 @@ const RoleApi = {
|
||||
data,
|
||||
});
|
||||
},
|
||||
update(data: editRequest) {
|
||||
return request<any, editResponse>({
|
||||
update(id: number | string | null, data: editRequest) {
|
||||
const requestData = { ...data, id };
|
||||
return request<any>({
|
||||
url: `${baseURL}`,
|
||||
method: "put",
|
||||
data,
|
||||
data: requestData,
|
||||
});
|
||||
},
|
||||
delete(data: delRequest) {
|
||||
return request<any, delResponse>({
|
||||
url: `${baseURL}`,
|
||||
method: "put",
|
||||
method: "delete",
|
||||
data,
|
||||
});
|
||||
},
|
||||
// 获取角色对应的菜单id
|
||||
getMenu(id: number) {
|
||||
return request<any>({
|
||||
url: `${baseURL}/menu`,
|
||||
method: "get",
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default RoleApi;
|
||||
@@ -226,3 +235,4 @@ export interface delResponse {
|
||||
[property: string]: any;
|
||||
}
|
||||
/** delete end */
|
||||
|
||||
|
||||
55
src/api/account/shopMerchant.ts
Normal file
55
src/api/account/shopMerchant.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import request from "@/utils/request";
|
||||
import { Account_BaseUrl } from "@/api/config";
|
||||
const baseURL = Account_BaseUrl + "/admin/shopMerchant";
|
||||
const API = {
|
||||
get(shopId: string | number) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "get",
|
||||
headers: {
|
||||
shopId: shopId
|
||||
}
|
||||
});
|
||||
},
|
||||
edit(shopId: string | number, data: shopMerchantType) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
headers: {
|
||||
shopId: shopId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
export default API;
|
||||
/**
|
||||
* ShopMerchantEditDTO
|
||||
*/
|
||||
export interface shopMerchantType {
|
||||
/**
|
||||
* 支付宝appid
|
||||
*/
|
||||
alipaySmallAppid: null | string;
|
||||
/**
|
||||
* 商户应用id
|
||||
*/
|
||||
appId: null | string;
|
||||
/**
|
||||
* 商户秘钥
|
||||
*/
|
||||
appSecret: null | string;
|
||||
/**
|
||||
* 支付密码
|
||||
*/
|
||||
payPassword: null | string;
|
||||
/**
|
||||
* 支付系统商户id
|
||||
*/
|
||||
storeId: null | string;
|
||||
/**
|
||||
* 微信appid
|
||||
*/
|
||||
wechatSmallAppid: null | string;
|
||||
[property: string]: any;
|
||||
}
|
||||
160
src/api/account/shopUser.ts
Normal file
160
src/api/account/shopUser.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import request from "@/utils/request";
|
||||
import { Account_BaseUrl } from "@/api/config";
|
||||
const baseURL = Account_BaseUrl + "/admin/shopUser";
|
||||
const API = {
|
||||
// 获取店铺用户概述信息
|
||||
getSummary(data: getSummaryRequest) {
|
||||
return request({
|
||||
url: `${baseURL}/summary`,
|
||||
method: "get",
|
||||
params: data
|
||||
});
|
||||
},
|
||||
getList(data: getListRequest) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "get",
|
||||
params: data
|
||||
});
|
||||
},
|
||||
edit(shopId: string | number, data: editRequest) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
headers: {
|
||||
shopId: shopId
|
||||
}
|
||||
});
|
||||
},
|
||||
// 店铺用户余额修改
|
||||
editMoney(shopId: string | number, data: editMoneyRequest) {
|
||||
return request({
|
||||
url: `${baseURL}/money`,
|
||||
method: "put",
|
||||
data: data,
|
||||
headers: {
|
||||
shopId: shopId
|
||||
}
|
||||
});
|
||||
},
|
||||
add(shopId: string | number, data: addRequest) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
headers: {
|
||||
shopId: shopId
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
export default API;
|
||||
|
||||
export interface getSummaryRequest {
|
||||
/**
|
||||
* 0 非vip 1 vip
|
||||
*/
|
||||
isVip?: number;
|
||||
[property: string]: any;
|
||||
}
|
||||
export interface getListRequest {
|
||||
/**
|
||||
* 0 非vip 1 vip
|
||||
*/
|
||||
isVip?: number;
|
||||
/**
|
||||
* 昵称或手机号
|
||||
*/
|
||||
key?: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* ShopUserEditDTO
|
||||
*/
|
||||
export interface editRequest {
|
||||
/**
|
||||
* 生日
|
||||
*/
|
||||
birthDay?: null | string;
|
||||
/**
|
||||
* 对应shopUserid
|
||||
*/
|
||||
id: number | null;
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
nickName?: null | string;
|
||||
/**
|
||||
* 性别 0女 1男
|
||||
*/
|
||||
sex?: number | null;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* ShopUserMoneyEditDTO
|
||||
*/
|
||||
export interface editMoneyRequest {
|
||||
/**
|
||||
* 对应shopUserid
|
||||
*/
|
||||
id: number | null;
|
||||
/**
|
||||
* 浮动金额
|
||||
*/
|
||||
money: number | null;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: null | string;
|
||||
/**
|
||||
* 0减少 1增加
|
||||
*/
|
||||
type: number | null;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* ShopUserAddDTO
|
||||
*/
|
||||
export interface addRequest {
|
||||
/**
|
||||
* 账户积分
|
||||
*/
|
||||
accountPoints?: number | null;
|
||||
/**
|
||||
* 钱包余额
|
||||
*/
|
||||
amount?: number | null;
|
||||
/**
|
||||
* 会员生日
|
||||
*/
|
||||
birthDay?: null | string;
|
||||
/**
|
||||
* 用户头像
|
||||
*/
|
||||
headImg?: null | string;
|
||||
/**
|
||||
* 是否会员,
|
||||
*/
|
||||
isVip?: number | null;
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
nickName: null | string;
|
||||
/**
|
||||
* 电话号码
|
||||
*/
|
||||
phone: null | string;
|
||||
/**
|
||||
* 0-女 1男
|
||||
*/
|
||||
sex?: number | null;
|
||||
/**
|
||||
* 用户Id
|
||||
*/
|
||||
userId: number | null;
|
||||
[property: string]: any;
|
||||
}
|
||||
128
src/api/account/table.ts
Normal file
128
src/api/account/table.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import request from "@/utils/request";
|
||||
import { Account_BaseUrl } from "@/api/config";
|
||||
const baseURL = Account_BaseUrl + "/admin/shopTable";
|
||||
const API = {
|
||||
// 批量生成桌码
|
||||
fasetAdd(num: number) {
|
||||
return request({
|
||||
url: `${baseURL}/code`,
|
||||
method: "post",
|
||||
data: { num },
|
||||
});
|
||||
},
|
||||
getList(data: getListRequest) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "get",
|
||||
params: data
|
||||
});
|
||||
},
|
||||
edit(data: editRequest) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
add(data: addRequest) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
},
|
||||
delete(id: number | string) {
|
||||
return request({
|
||||
url: `${baseURL}`,
|
||||
method: "post",
|
||||
data: { id },
|
||||
});
|
||||
}
|
||||
}
|
||||
export default API;
|
||||
/**
|
||||
* ShopTableAddDTO
|
||||
*/
|
||||
export interface addRequest {
|
||||
/**
|
||||
* 区域id
|
||||
*/
|
||||
areaId?: number | null;
|
||||
/**
|
||||
* 是否自动清台
|
||||
*/
|
||||
autoClear?: number | null;
|
||||
/**
|
||||
* 客座数
|
||||
*/
|
||||
capacity: number | null;
|
||||
/**
|
||||
* 结束数字
|
||||
*/
|
||||
end: number | null;
|
||||
/**
|
||||
* 台桌前缀
|
||||
*/
|
||||
sign: null | string;
|
||||
/**
|
||||
* 起始数字
|
||||
*/
|
||||
start: number | null;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* ShopTableDTO
|
||||
*/
|
||||
export interface editRequest {
|
||||
/**
|
||||
* 区域Id
|
||||
*/
|
||||
areaId?: number | null;
|
||||
/**
|
||||
* 自动清台 0手动 1自动
|
||||
*/
|
||||
autoClear?: number | null;
|
||||
/**
|
||||
* 自增id
|
||||
*/
|
||||
id: number | null;
|
||||
/**
|
||||
* 是否接受网络预定
|
||||
*/
|
||||
isPredate?: number | null;
|
||||
/**
|
||||
* 客座数,允许的客座数量
|
||||
*/
|
||||
maxCapacity?: number | null;
|
||||
name?: null | string;
|
||||
/**
|
||||
* 网络预定台桌支付金额
|
||||
*/
|
||||
predateAmount?: number | null;
|
||||
/**
|
||||
* 二维码
|
||||
*/
|
||||
qrcode?: null | string;
|
||||
/**
|
||||
* 台桌排序
|
||||
*/
|
||||
sort?: number | null;
|
||||
/**
|
||||
* idle-空闲 using-使用中 subscribe预定,closed--关台, opening 开台中,cleaning 台桌清理中
|
||||
*/
|
||||
status?: null | string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
export interface getListRequest {
|
||||
/**
|
||||
* 区域id
|
||||
*/
|
||||
areaId?: number;
|
||||
/**
|
||||
* 桌码
|
||||
*/
|
||||
tableCode?: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
@@ -216,7 +216,7 @@ export default UserAPI;
|
||||
export interface UserInfo {
|
||||
/** 用户ID */
|
||||
userId?: number;
|
||||
|
||||
id?: number | string;
|
||||
/** 用户名 */
|
||||
username?: string;
|
||||
|
||||
|
||||
@@ -8,37 +8,58 @@
|
||||
<template v-if="typeof item === 'string'">
|
||||
<!-- 新增 -->
|
||||
<template v-if="item === 'add'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="success" icon="plus"
|
||||
@click="handleToolbar(item)">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="success"
|
||||
icon="plus"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 删除 -->
|
||||
<template v-else-if="item === 'delete'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="danger" icon="delete"
|
||||
:disabled="removeIds.length === 0" @click="handleToolbar(item)">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="danger"
|
||||
icon="delete"
|
||||
:disabled="removeIds.length === 0"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 导入 -->
|
||||
<template v-else-if="item === 'import'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="default" icon="upload"
|
||||
@click="handleToolbar(item)">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="default"
|
||||
icon="upload"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
导入
|
||||
</el-button>
|
||||
</template>
|
||||
<!-- 导出 -->
|
||||
<template v-else-if="item === 'export'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]" type="default" icon="download"
|
||||
@click="handleToolbar(item)">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
type="default"
|
||||
icon="download"
|
||||
@click="handleToolbar(item)"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 其他 -->
|
||||
<template v-else-if="typeof item === 'object'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]" :icon="item.icon"
|
||||
:type="item.type ?? 'default'" @click="handleToolbar(item.name)">
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
||||
:icon="item.icon"
|
||||
:type="item.type ?? 'default'"
|
||||
@click="handleToolbar(item.name)"
|
||||
>
|
||||
{{ item.text }}
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -67,36 +88,68 @@
|
||||
</template>
|
||||
<!-- 导出 -->
|
||||
<template v-else-if="item === 'exports'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:export`]" icon="download" circle title="导出"
|
||||
@click="handleToolbar(item)" />
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:export`]"
|
||||
icon="download"
|
||||
circle
|
||||
title="导出"
|
||||
@click="handleToolbar(item)"
|
||||
/>
|
||||
</template>
|
||||
<!-- 导入 -->
|
||||
<template v-else-if="item === 'imports'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:import`]" icon="upload" circle title="导入"
|
||||
@click="handleToolbar(item)" />
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:import`]"
|
||||
icon="upload"
|
||||
circle
|
||||
title="导入"
|
||||
@click="handleToolbar(item)"
|
||||
/>
|
||||
</template>
|
||||
<!-- 搜索 -->
|
||||
<template v-else-if="item === 'search'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:query`]" icon="search" circle title="搜索"
|
||||
@click="handleToolbar(item)" />
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:query`]"
|
||||
icon="search"
|
||||
circle
|
||||
title="搜索"
|
||||
@click="handleToolbar(item)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 其他 -->
|
||||
<template v-else-if="typeof item === 'object'">
|
||||
<template v-if="item.auth">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]" :icon="item.icon" circle
|
||||
:title="item.title" @click="handleToolbar(item.name)" />
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
||||
:icon="item.icon"
|
||||
circle
|
||||
:title="item.title"
|
||||
@click="handleToolbar(item.name)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-button :icon="item.icon" circle :title="item.title" @click="handleToolbar(item.name)" />
|
||||
<el-button
|
||||
:icon="item.icon"
|
||||
circle
|
||||
:title="item.title"
|
||||
@click="handleToolbar(item.name)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 列表 -->
|
||||
<el-table ref="tableRef" v-loading="loading" v-bind="contentConfig.table" :data="pageData" :row-key="pk"
|
||||
@selection-change="handleSelectionChange" @filter-change="handleFilterChange">
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
v-bind="contentConfig.table"
|
||||
:data="pageData"
|
||||
:row-key="pk"
|
||||
@selection-change="handleSelectionChange"
|
||||
@filter-change="handleFilterChange"
|
||||
>
|
||||
<template v-for="col in cols" :key="col">
|
||||
<el-table-column v-if="col.show" v-bind="col">
|
||||
<template #default="scope">
|
||||
@@ -105,15 +158,24 @@
|
||||
<template v-if="col.prop">
|
||||
<template v-if="Array.isArray(scope.row[col.prop])">
|
||||
<template v-for="(item, index) in scope.row[col.prop]" :key="item">
|
||||
<el-image :src="item" :preview-src-list="scope.row[col.prop]" :initial-index="index"
|
||||
:preview-teleported="true" :style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40
|
||||
}px`" />
|
||||
<el-image
|
||||
:src="item"
|
||||
:preview-src-list="scope.row[col.prop]"
|
||||
:initial-index="index"
|
||||
:preview-teleported="true"
|
||||
:style="`width: ${col.imageWidth ?? 40}px; height: ${
|
||||
col.imageHeight ?? 40
|
||||
}px`"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-image :src="scope.row[col.prop]" :preview-src-list="[scope.row[col.prop]]"
|
||||
<el-image
|
||||
:src="scope.row[col.prop]"
|
||||
:preview-src-list="[scope.row[col.prop]]"
|
||||
:preview-teleported="true"
|
||||
:style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40}px`" />
|
||||
:style="`width: ${col.imageWidth ?? 40}px; height: ${col.imageHeight ?? 40}px`"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
@@ -135,20 +197,30 @@
|
||||
<template v-else-if="col.templet === 'switch'">
|
||||
<template v-if="col.prop">
|
||||
<!-- pageData.length>0: 解决el-switch组件会在表格初始化的时候触发一次change事件 -->
|
||||
<el-switch v-model="scope.row[col.prop]" :active-value="col.activeValue ?? 1"
|
||||
:inactive-value="col.inactiveValue ?? 0" :inline-prompt="true" :active-text="col.activeText ?? ''"
|
||||
:inactive-text="col.inactiveText ?? ''" :validate-event="false"
|
||||
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)" @change="
|
||||
<el-switch
|
||||
v-model="scope.row[col.prop]"
|
||||
:active-value="col.activeValue ?? 1"
|
||||
:inactive-value="col.inactiveValue ?? 0"
|
||||
:inline-prompt="true"
|
||||
:active-text="col.activeText ?? ''"
|
||||
:inactive-text="col.inactiveText ?? ''"
|
||||
:validate-event="false"
|
||||
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
|
||||
@change="
|
||||
pageData.length > 0 && handleModify(col.prop, scope.row[col.prop], scope.row)
|
||||
" />
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 生成输入框组件 -->
|
||||
<template v-else-if="col.templet === 'input'">
|
||||
<template v-if="col.prop">
|
||||
<el-input v-model="scope.row[col.prop]" :type="col.inputType ?? 'text'"
|
||||
<el-input
|
||||
v-model="scope.row[col.prop]"
|
||||
:type="col.inputType ?? 'text'"
|
||||
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
|
||||
@blur="handleModify(col.prop, scope.row[col.prop], scope.row)" />
|
||||
@blur="handleModify(col.prop, scope.row[col.prop], scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 格式化为价格 -->
|
||||
@@ -180,7 +252,7 @@
|
||||
{{
|
||||
scope.row[col.prop]
|
||||
? useDateFormat(scope.row[col.prop], col.dateFormat ?? "YYYY-MM-DD HH:mm:ss")
|
||||
.value
|
||||
.value
|
||||
: ""
|
||||
}}
|
||||
</template>
|
||||
@@ -191,32 +263,68 @@
|
||||
<template v-if="typeof item === 'string'">
|
||||
<!-- 编辑/删除 -->
|
||||
<template v-if="item === 'edit' || item === 'delete'">
|
||||
<el-button v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
:type="item === 'edit' ? 'primary' : 'danger'" :icon="item" size="small" link @click="
|
||||
<el-button
|
||||
v-hasPerm="[`${contentConfig.pageName}:${item}`]"
|
||||
:type="item === 'edit' ? 'primary' : 'danger'"
|
||||
:icon="item"
|
||||
size="small"
|
||||
link
|
||||
@click="
|
||||
handleOperat({
|
||||
name: item,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
})
|
||||
">
|
||||
"
|
||||
>
|
||||
{{ item === "edit" ? "编辑" : "删除" }}
|
||||
</el-button>
|
||||
</template>
|
||||
</template>
|
||||
<!-- 其他 -->
|
||||
<template v-else-if="typeof item === 'object'">
|
||||
<el-button v-if="item.render === undefined || item.render(scope.row)" v-bind="item.auth ? { 'v-hasPerm': [`${contentConfig.pageName}:${item.auth}`] } : {}
|
||||
" :icon="item.icon" :type="item.type ?? 'primary'" size="small" link @click="
|
||||
handleOperat({
|
||||
name: item.name,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
})
|
||||
">
|
||||
{{ item.text }}
|
||||
</el-button>
|
||||
<el-dropdown>
|
||||
<el-button
|
||||
v-if="item.render === undefined || item.render(scope.row)"
|
||||
v-bind="
|
||||
item.auth ? { 'v-hasPerm': [`${contentConfig.pageName}:${item.auth}`] } : {}
|
||||
"
|
||||
:icon="item.icon"
|
||||
:type="item.type ?? 'primary'"
|
||||
size="small"
|
||||
link
|
||||
@click="
|
||||
handleOperat({
|
||||
name: item.name,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
})
|
||||
"
|
||||
>
|
||||
{{ item.text }}
|
||||
</el-button>
|
||||
<template #dropdown v-if="item.options && item.options.length > 0">
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@click="
|
||||
handleOperat({
|
||||
name: item.name,
|
||||
row: scope.row,
|
||||
column: scope.column,
|
||||
$index: scope.$index,
|
||||
command: opt.command ? opt.command : '',
|
||||
})
|
||||
"
|
||||
v-for="opt in item.options"
|
||||
:key="opt.value"
|
||||
>
|
||||
{{ opt.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
@@ -232,18 +340,33 @@
|
||||
<template v-if="showPagination">
|
||||
<el-scrollbar>
|
||||
<div class="mt-[12px]">
|
||||
<el-pagination v-bind="pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
||||
<el-pagination
|
||||
v-bind="pagination"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</template>
|
||||
<!-- 导出弹窗 -->
|
||||
<el-dialog v-model="exportsModalVisible" :align-center="true" title="导出数据" width="600px" style="padding-right: 0"
|
||||
@close="handleCloseExportsModal">
|
||||
<el-dialog
|
||||
v-model="exportsModalVisible"
|
||||
:align-center="true"
|
||||
title="导出数据"
|
||||
width="600px"
|
||||
style="padding-right: 0"
|
||||
@close="handleCloseExportsModal"
|
||||
>
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form ref="exportsFormRef" label-width="auto" style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="exportsFormData" :rules="exportsFormRules">
|
||||
<el-form
|
||||
ref="exportsFormRef"
|
||||
label-width="auto"
|
||||
style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="exportsFormData"
|
||||
:rules="exportsFormRules"
|
||||
>
|
||||
<el-form-item label="文件名" prop="filename">
|
||||
<el-input v-model="exportsFormData.filename" clearable />
|
||||
</el-form-item>
|
||||
@@ -253,10 +376,16 @@
|
||||
<el-form-item label="数据源" prop="origin">
|
||||
<el-select v-model="exportsFormData.origin">
|
||||
<el-option label="当前数据 (当前页的数据)" :value="ExportsOriginEnum.CURRENT" />
|
||||
<el-option label="选中数据 (所有选中的数据)" :value="ExportsOriginEnum.SELECTED"
|
||||
:disabled="selectionData.length <= 0" />
|
||||
<el-option label="全量数据 (所有分页的数据)" :value="ExportsOriginEnum.REMOTE"
|
||||
:disabled="contentConfig.exportsAction === undefined" />
|
||||
<el-option
|
||||
label="选中数据 (所有选中的数据)"
|
||||
:value="ExportsOriginEnum.SELECTED"
|
||||
:disabled="selectionData.length <= 0"
|
||||
/>
|
||||
<el-option
|
||||
label="全量数据 (所有分页的数据)"
|
||||
:value="ExportsOriginEnum.REMOTE"
|
||||
:disabled="contentConfig.exportsAction === undefined"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="字段" prop="fields">
|
||||
@@ -277,17 +406,35 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 导入弹窗 -->
|
||||
<el-dialog v-model="importModalVisible" :align-center="true" title="导入数据" width="600px" style="padding-right: 0"
|
||||
@close="handleCloseImportModal">
|
||||
<el-dialog
|
||||
v-model="importModalVisible"
|
||||
:align-center="true"
|
||||
title="导入数据"
|
||||
width="600px"
|
||||
style="padding-right: 0"
|
||||
@close="handleCloseImportModal"
|
||||
>
|
||||
<!-- 滚动 -->
|
||||
<el-scrollbar max-height="60vh">
|
||||
<!-- 表单 -->
|
||||
<el-form ref="importFormRef" label-width="auto" style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="importFormData" :rules="importFormRules">
|
||||
<el-form
|
||||
ref="importFormRef"
|
||||
label-width="auto"
|
||||
style="padding-right: var(--el-dialog-padding-primary)"
|
||||
:model="importFormData"
|
||||
:rules="importFormRules"
|
||||
>
|
||||
<el-form-item label="文件名" prop="files">
|
||||
<el-upload ref="uploadRef" v-model:file-list="importFormData.files" class="w-full"
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
v-model:file-list="importFormData.files"
|
||||
class="w-full"
|
||||
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||
:drag="true" :limit="1" :auto-upload="false" :on-exceed="handleFileExceed">
|
||||
:drag="true"
|
||||
:limit="1"
|
||||
:auto-upload="false"
|
||||
:on-exceed="handleFileExceed"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">
|
||||
<span>将文件拖到此处,或</span>
|
||||
@@ -296,8 +443,13 @@
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
*.xlsx / *.xls
|
||||
<el-link v-if="contentConfig.importTemplate" type="primary" icon="download" :underline="false"
|
||||
@click="handleDownloadTemplate">
|
||||
<el-link
|
||||
v-if="contentConfig.importTemplate"
|
||||
type="primary"
|
||||
icon="download"
|
||||
:underline="false"
|
||||
@click="handleDownloadTemplate"
|
||||
>
|
||||
下载模板
|
||||
</el-link>
|
||||
</div>
|
||||
@@ -309,7 +461,11 @@
|
||||
<!-- 弹窗底部操作按钮 -->
|
||||
<template #footer>
|
||||
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||
<el-button type="primary" :disabled="importFormData.files.length === 0" @click="handleImportSubmit">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="importFormData.files.length === 0"
|
||||
@click="handleImportSubmit"
|
||||
>
|
||||
确 定
|
||||
</el-button>
|
||||
<el-button @click="handleCloseImportModal">取 消</el-button>
|
||||
@@ -785,10 +941,10 @@ function fetchPageData(formData: IObject = {}, isRestart = false) {
|
||||
.indexAction(
|
||||
showPagination
|
||||
? {
|
||||
[request.pageName]: pagination.currentPage,
|
||||
[request.limitName]: pagination.pageSize,
|
||||
...formData,
|
||||
}
|
||||
[request.pageName]: pagination.currentPage,
|
||||
[request.limitName]: pagination.pageSize,
|
||||
...formData,
|
||||
}
|
||||
: formData
|
||||
)
|
||||
.then((data) => {
|
||||
|
||||
@@ -78,7 +78,10 @@
|
||||
<template v-else-if="item.type === 'date-picker'">
|
||||
<el-date-picker v-model="formData[item.prop]" v-bind="item.attrs" />
|
||||
</template>
|
||||
|
||||
<!-- 图片上传 -->
|
||||
<template v-else-if="item.type === 'UpImage'">
|
||||
<SingleImageUpload v-model="formData[item.prop]" />
|
||||
</template>
|
||||
<!-- Text 文本 -->
|
||||
<template v-else-if="item.type === 'text'">
|
||||
<el-text v-bind="item.attrs">
|
||||
@@ -237,6 +240,7 @@ const props = defineProps<{
|
||||
// 自定义事件
|
||||
const emit = defineEmits<{
|
||||
submitClick: [];
|
||||
formDataChange: [string, any];
|
||||
}>();
|
||||
const pk = props.modalConfig.pk ?? "id";
|
||||
const modalVisible = ref(false);
|
||||
@@ -256,6 +260,7 @@ for (const item of formItems) {
|
||||
watch(
|
||||
() => formData[item.prop],
|
||||
(newValue, oldValue) => {
|
||||
emit("formDataChange", item.prop, newValue);
|
||||
item.watch && item.watch(newValue, oldValue, formData, formItems);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface IOperatData {
|
||||
row: IObject;
|
||||
column: IObject;
|
||||
$index: number;
|
||||
command?: string | number;
|
||||
}
|
||||
|
||||
export interface ISearchConfig {
|
||||
@@ -185,6 +186,11 @@ export interface IContentConfig<T = any> {
|
||||
text: string;
|
||||
type?: "primary" | "success" | "warning" | "danger" | "info";
|
||||
render?: (row: IObject) => boolean;
|
||||
options?: {
|
||||
label: string;
|
||||
command: string | number,
|
||||
value?: any;
|
||||
}[];
|
||||
}
|
||||
>;
|
||||
// filter值拼接符
|
||||
|
||||
@@ -321,7 +321,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
{
|
||||
path: "table",
|
||||
component: () => import("@/views/tool/table.vue"),
|
||||
component: () => import("@/views/tool/table/index.vue"),
|
||||
name: "table",
|
||||
meta: {
|
||||
title: "台桌管理",
|
||||
@@ -463,7 +463,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
children: [
|
||||
{
|
||||
path: "index",
|
||||
component: () => import("@/views/user/index.vue"),
|
||||
component: () => import("@/views/user/list/index.vue"),
|
||||
name: "userIndex",
|
||||
meta: {
|
||||
title: "用户列表",
|
||||
@@ -473,7 +473,7 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
},
|
||||
{
|
||||
path: "active",
|
||||
component: () => import("@/views/user/active.vue"),
|
||||
component: () => import("@/views/user/active/index.vue"),
|
||||
name: "userActive",
|
||||
meta: {
|
||||
title: "活动管理",
|
||||
|
||||
@@ -4,6 +4,7 @@ import { store } from "@/store";
|
||||
import router from "@/router";
|
||||
|
||||
import MenuAPI, { type RouteVO } from "@/api/account/menu";
|
||||
const isTest = true//是否是本地调试
|
||||
const modules = import.meta.glob("../../views/**/**.vue");
|
||||
const Layout = () => import("@/layout/index.vue");
|
||||
|
||||
@@ -16,6 +17,8 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
// 路由是否加载完成
|
||||
const isRoutesLoaded = ref(false);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取后台动态路由数据,解析并注册到全局路由
|
||||
*
|
||||
@@ -25,12 +28,18 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
return new Promise<RouteRecordRaw[]>((resolve, reject) => {
|
||||
MenuAPI.getRoutes()
|
||||
.then((data) => {
|
||||
// const dynamicRoutes = parseDynamicRoutes(data);
|
||||
// routes.value = [...constantRoutes, ...dynamicRoutes];
|
||||
// isRoutesLoaded.value = true;
|
||||
// resolve(dynamicRoutes);
|
||||
isRoutesLoaded.value = true;
|
||||
resolve(constantRoutes);
|
||||
if (!isTest) {
|
||||
const dynamicRoutes = parseDynamicRoutes(data.filter(v => v.type == 0));
|
||||
routes.value = [...constantRoutes, ...dynamicRoutes];
|
||||
console.log(routes.value);
|
||||
isRoutesLoaded.value = true;
|
||||
resolve(dynamicRoutes);
|
||||
} else {
|
||||
|
||||
isRoutesLoaded.value = true;
|
||||
resolve(constantRoutes);
|
||||
}
|
||||
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
@@ -55,7 +64,9 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
*/
|
||||
const resetRouter = () => {
|
||||
// 清空本地存储的路由和菜单数据
|
||||
// routes.value = [];
|
||||
if (!isTest) {
|
||||
routes.value = [];
|
||||
}
|
||||
routes.value = constantRoutes;
|
||||
mixedLayoutLeftRoutes.value = [];
|
||||
// 从 Vue Router 中移除所有动态注册的路由
|
||||
@@ -90,21 +101,33 @@ export const usePermissionStore = defineStore("permission", () => {
|
||||
*/
|
||||
const parseDynamicRoutes = (rawRoutes: RouteVO[]): RouteRecordRaw[] => {
|
||||
const parsedRoutes: RouteRecordRaw[] = [];
|
||||
|
||||
rawRoutes.forEach((route) => {
|
||||
const normalizedRoute = { ...route } as RouteRecordRaw;
|
||||
console.log(route.path)
|
||||
const normalizedRoute = {
|
||||
path: route.path,
|
||||
meta: {
|
||||
title: route.title,
|
||||
icon: route.icon,
|
||||
keepAlive: route.cache,
|
||||
alwaysShow: route.path && route.path.startsWith('/') ? true : false,
|
||||
hidden: route.hidden,
|
||||
},
|
||||
children: route.children,
|
||||
component: route.component,
|
||||
} as RouteRecordRaw;
|
||||
|
||||
// 处理组件路径
|
||||
normalizedRoute.component =
|
||||
normalizedRoute.component?.toString() === "Layout"
|
||||
!normalizedRoute.component
|
||||
? Layout
|
||||
: modules[`../../views/${normalizedRoute.component}.vue`] ||
|
||||
modules["../../views/error-page/404.vue"];
|
||||
|
||||
modules["../../views/error-page/404.vue"];
|
||||
// 递归解析子路由
|
||||
if (normalizedRoute.children) {
|
||||
// normalizedRoute.redirect = (!normalizedRoute.redirect && route.children.length <= 1) ? normalizedRoute.children[0].path : normalizedRoute.path
|
||||
normalizedRoute.children = parseDynamicRoutes(route.children);
|
||||
}
|
||||
console.log(normalizedRoute)
|
||||
|
||||
parsedRoutes.push(normalizedRoute);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { ResultEnum } from "@/enums/ResultEnum";
|
||||
import { getToken } from "@/utils/auth";
|
||||
import router from "@/router";
|
||||
|
||||
// 创建 axios 实例
|
||||
const service = axios.create({
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
@@ -16,6 +15,7 @@ const service = axios.create({
|
||||
// 请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
console.log(config);
|
||||
const accessToken = getToken();
|
||||
// 如果 Authorization 设置为 no-auth,则不携带 Token,用于登录、刷新 Token 等接口
|
||||
if (config.headers.Authorization !== "no-auth" && accessToken) {
|
||||
@@ -23,6 +23,7 @@ service.interceptors.request.use(
|
||||
} else {
|
||||
delete config.headers.token;
|
||||
}
|
||||
config.headers.shopId = config.headers.shopId || useUserStoreHook().userInfo.id;
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
@@ -37,6 +38,16 @@ service.interceptors.response.use(
|
||||
}
|
||||
|
||||
const { code, data, msg } = response.data;
|
||||
if (data) {
|
||||
// 处理后台返回分页相关数据为字符串类型时,elementui组件警告
|
||||
const kes = ['pageNumber', 'pageSize', 'totalPage', 'totalRow'];
|
||||
const keys = Object.keys(data);
|
||||
for (let i of kes) {
|
||||
if (keys.includes(i)) {
|
||||
data[i] = data[i] * 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (code === ResultEnum.SUCCESS || code === undefined || code === null) {
|
||||
return data ? data : response.data;
|
||||
}
|
||||
|
||||
93
src/utils/websocket copy.ts
Normal file
93
src/utils/websocket copy.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { Client } from "@stomp/stompjs";
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
class WebSocketManager {
|
||||
private client: Client | null = null;
|
||||
private messageHandlers: Map<string, ((message: string) => void)[]> = new Map();
|
||||
private reconnectAttempts = 0;
|
||||
private maxReconnectAttempts = 3; // 自定义最大重试次数
|
||||
private reconnectDelay = 5000; // 重试延迟(单位:毫秒)
|
||||
|
||||
// 初始化 WebSocket 客户端
|
||||
setupWebSocket() {
|
||||
const endpoint = import.meta.env.VITE_APP_WS_ENDPOINT;
|
||||
|
||||
// 如果没有配置 WebSocket 端点或显式关闭,直接返回
|
||||
if (!endpoint) {
|
||||
console.log("WebSocket 已被禁用,如需打开请在配置文件中配置 VITE_APP_WS_ENDPOINT");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.client && this.client.connected) {
|
||||
console.log("客户端已存在并且连接正常");
|
||||
return this.client;
|
||||
}
|
||||
|
||||
this.client = new Client({
|
||||
brokerURL: endpoint,
|
||||
connectHeaders: {
|
||||
Authorization: getToken(),
|
||||
},
|
||||
heartbeatIncoming: 30000,
|
||||
heartbeatOutgoing: 30000,
|
||||
reconnectDelay: 0, // 设置为 0 禁用重连
|
||||
onConnect: () => {
|
||||
console.log(`连接到 WebSocket 服务器: ${endpoint}`);
|
||||
this.reconnectAttempts = 0; // 重置重连计数
|
||||
this.messageHandlers.forEach((handlers, topic) => {
|
||||
handlers.forEach((handler) => {
|
||||
this.subscribeToTopic(topic, handler);
|
||||
});
|
||||
});
|
||||
},
|
||||
onStompError: (frame) => {
|
||||
console.error(`连接错误: ${frame.headers["message"]}`);
|
||||
console.error(`错误详情: ${frame.body}`);
|
||||
},
|
||||
onDisconnect: () => {
|
||||
console.log(`WebSocket 连接已断开: ${endpoint}`);
|
||||
this.reconnectAttempts++;
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
console.log(`正在尝试重连... 尝试次数: ${this.reconnectAttempts}`);
|
||||
} else {
|
||||
console.log("重连次数已达上限,停止重连");
|
||||
this.client?.deactivate();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.client.activate();
|
||||
}
|
||||
|
||||
// 订阅主题
|
||||
public subscribeToTopic(topic: string, onMessage: (message: string) => void) {
|
||||
console.log(`正在订阅主题: ${topic}`);
|
||||
if (!this.client || !this.client.connected) {
|
||||
this.setupWebSocket();
|
||||
}
|
||||
|
||||
if (this.messageHandlers.has(topic)) {
|
||||
this.messageHandlers.get(topic)?.push(onMessage);
|
||||
} else {
|
||||
this.messageHandlers.set(topic, [onMessage]);
|
||||
}
|
||||
|
||||
if (this.client?.connected) {
|
||||
this.client.subscribe(topic, (message) => {
|
||||
const handlers = this.messageHandlers.get(topic);
|
||||
handlers?.forEach((handler) => handler(message.body));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 断开 WebSocket 连接
|
||||
public disconnect() {
|
||||
if (this.client) {
|
||||
console.log("断开 WebSocket 连接");
|
||||
this.client.deactivate();
|
||||
this.client = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new WebSocketManager();
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Client } from "@stomp/stompjs";
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
class WebSocketManager {
|
||||
private client: Client | null = null;
|
||||
private client: WebSocket | null = null;
|
||||
private connected: boolean = false;
|
||||
private onMessage: (message: any) => void = function () { };
|
||||
private messageHandlers: Map<string, ((message: string) => void)[]> = new Map();
|
||||
private type: string = 'manage';
|
||||
private reconnectAttempts = 0;
|
||||
private maxReconnectAttempts = 3; // 自定义最大重试次数
|
||||
private reconnectDelay = 5000; // 重试延迟(单位:毫秒)
|
||||
@@ -18,73 +19,52 @@ class WebSocketManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.client && this.client.connected) {
|
||||
if (this.client && this.connected) {
|
||||
console.log("客户端已存在并且连接正常");
|
||||
return this.client;
|
||||
}
|
||||
|
||||
this.client = new Client({
|
||||
brokerURL: endpoint,
|
||||
connectHeaders: {
|
||||
Authorization: getToken(),
|
||||
},
|
||||
heartbeatIncoming: 30000,
|
||||
heartbeatOutgoing: 30000,
|
||||
reconnectDelay: 0, // 设置为 0 禁用重连
|
||||
onConnect: () => {
|
||||
console.log(`连接到 WebSocket 服务器: ${endpoint}`);
|
||||
this.reconnectAttempts = 0; // 重置重连计数
|
||||
this.messageHandlers.forEach((handlers, topic) => {
|
||||
handlers.forEach((handler) => {
|
||||
this.subscribeToTopic(topic, handler);
|
||||
});
|
||||
});
|
||||
},
|
||||
onStompError: (frame) => {
|
||||
console.error(`连接错误: ${frame.headers["message"]}`);
|
||||
console.error(`错误详情: ${frame.body}`);
|
||||
},
|
||||
onDisconnect: () => {
|
||||
console.log(`WebSocket 连接已断开: ${endpoint}`);
|
||||
this.reconnectAttempts++;
|
||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||
console.log(`正在尝试重连... 尝试次数: ${this.reconnectAttempts}`);
|
||||
} else {
|
||||
console.log("重连次数已达上限,停止重连");
|
||||
this.client?.deactivate();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.client.activate();
|
||||
this.client = new WebSocket(endpoint)
|
||||
this.client.onopen = () => {
|
||||
this.connected = true;
|
||||
console.log("WebSocket 连接已建立");
|
||||
this.sendMessage('test')
|
||||
};
|
||||
this.client.onclose = () => {
|
||||
this.connected = false;
|
||||
console.log("WebSocket 连接已断开");
|
||||
};
|
||||
this.client.onerror = (error) => {
|
||||
console.error("WebSocket 发生错误:", error);
|
||||
};
|
||||
this.client.onmessage = (event) => {
|
||||
const message = event.data;
|
||||
this.getMessage(message)
|
||||
};
|
||||
}
|
||||
private getMessage(message: any) {
|
||||
console.log("收到消息:", message);
|
||||
}
|
||||
|
||||
// 订阅主题
|
||||
public subscribeToTopic(topic: string, onMessage: (message: string) => void) {
|
||||
console.log(`正在订阅主题: ${topic}`);
|
||||
if (!this.client || !this.client.connected) {
|
||||
if (!this.client || !this.connected) {
|
||||
this.setupWebSocket();
|
||||
}
|
||||
|
||||
if (this.messageHandlers.has(topic)) {
|
||||
this.messageHandlers.get(topic)?.push(onMessage);
|
||||
} else {
|
||||
this.messageHandlers.set(topic, [onMessage]);
|
||||
}
|
||||
|
||||
if (this.client?.connected) {
|
||||
this.client.subscribe(topic, (message) => {
|
||||
const handlers = this.messageHandlers.get(topic);
|
||||
handlers?.forEach((handler) => handler(message.body));
|
||||
});
|
||||
if (this.connected) {
|
||||
this.onMessage = onMessage;
|
||||
}
|
||||
}
|
||||
public sendMessage(message: any) {
|
||||
if (this.client) {
|
||||
this.client.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
// 断开 WebSocket 连接
|
||||
public disconnect() {
|
||||
if (this.client) {
|
||||
console.log("断开 WebSocket 连接");
|
||||
this.client.deactivate();
|
||||
this.client.close();
|
||||
this.client = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,47 +48,67 @@
|
||||
|
||||
<el-table-column label="图标" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.icon" :class="`i-svg:${scope.row.icon}`" />
|
||||
<template v-if="scope.row.icon && scope.row.icon.startsWith('el-icon')">
|
||||
<el-icon style="vertical-align: -0.15em">
|
||||
<component :is="scope.row.icon.replace('el-icon-', '')" />
|
||||
</el-icon>
|
||||
</template>
|
||||
<template v-else-if="scope.row.icon">
|
||||
<svg-icon :icon-class="scope.row.icon" />
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="类型" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
v-if="scope.row.type === MenuTypeEnum.MENU && scope.row.path.startsWith('/')"
|
||||
type="warning"
|
||||
>
|
||||
目录
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="scope.row.type === MenuTypeEnum.MENU && !scope.row.path.startsWith('/')"
|
||||
type="success"
|
||||
>
|
||||
菜单
|
||||
</el-tag>
|
||||
<el-tag v-if="scope.row.type === MenuTypeEnum.BUTTON" type="danger">按钮</el-tag>
|
||||
<el-tag v-if="scope.row.type === MenuTypeEnum.EXTLINK" type="info">外链</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="left" width="150" prop="menuSort" />
|
||||
<el-table-column label="权限标识" align="center" width="200" prop="perm" />
|
||||
<el-table-column label="路由路径" align="left" width="250" prop="path" />
|
||||
<el-table-column label="组件路径" align="left" width="250" prop="component" />
|
||||
<el-table-column label="外链" align="left" width="80" prop="iFrame">
|
||||
<template #default="scope">
|
||||
{{ scope.row.iFrame ? "是" : "否" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="缓存" align="left" width="80" prop="iFrame">
|
||||
<el-table-column label="缓存" align="left" width="80" prop="cache">
|
||||
<template #default="scope">
|
||||
{{ scope.row.iFrame ? "是" : "否" }}
|
||||
{{ scope.row.cache ? "是" : "否" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="可见" align="left" width="80" prop="iFrame">
|
||||
<template #default="scope">
|
||||
{{ scope.row.iFrame ? "是" : "否" }}
|
||||
{{ scope.row.hidden ? "否" : "是" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商家可用" align="left" width="80" prop="iFrame">
|
||||
<el-table-column label="创建日期" align="left" prop="createTime">
|
||||
<template #default="scope">
|
||||
{{ scope.row.iFrame ? "是" : "否" }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="商家可用" align="left" width="80" prop="iFrame">
|
||||
<template #default="scope">
|
||||
{{ scope.row.iFrame ? "是" : "否" }}
|
||||
{{ scope.row.createTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" align="center" label="操作" width="220">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
v-if="scope.row.type == 'MENU' || scope.row.type == 'MENU'"
|
||||
v-if="scope.row.type == 0"
|
||||
v-hasPerm="['sys:menu:add']"
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
icon="plus"
|
||||
@click.stop="handleOpenDialog(scope.row.id)"
|
||||
@click.stop="handleOpenDialog(scope.row.menuId)"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
@@ -99,7 +119,7 @@
|
||||
link
|
||||
size="small"
|
||||
icon="edit"
|
||||
@click.stop="handleOpenDialog(undefined, scope.row.id)"
|
||||
@click.stop="handleOpenDialog(undefined, scope.row.menuId)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
@@ -109,7 +129,7 @@
|
||||
link
|
||||
size="small"
|
||||
icon="delete"
|
||||
@click.stop="handleDelete(scope.row.id)"
|
||||
@click.stop="handleDelete(scope.row.menuId)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
@@ -144,10 +164,10 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.iFrame == 1" label="外链地址" prop="path">
|
||||
<el-input v-model="formData.routePath" placeholder="请输入外链完整路径" />
|
||||
<el-input v-model="formData.path" placeholder="请输入外链完整路径" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="routeName">
|
||||
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="name">
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
路由名称
|
||||
@@ -161,10 +181,10 @@
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="formData.routeName" placeholder="User" />
|
||||
<el-input v-model="formData.name" placeholder="User" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="routePath">
|
||||
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="path">
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
路由路径
|
||||
@@ -181,10 +201,10 @@
|
||||
</template>
|
||||
<el-input
|
||||
v-if="formData.type == MenuTypeEnum.MENU"
|
||||
v-model="formData.routePath"
|
||||
v-model="formData.path"
|
||||
placeholder="system"
|
||||
/>
|
||||
<el-input v-else v-model="formData.routePath" placeholder="user" />
|
||||
<el-input v-else v-model="formData.path" placeholder="user" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.type == MenuTypeEnum.MENU" prop="component">
|
||||
@@ -208,85 +228,10 @@
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.type == MenuTypeEnum.MENU">
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
路由参数
|
||||
<el-tooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
组件页面使用 `useRoute().query.参数名` 获取路由参数值。
|
||||
</template>
|
||||
<el-icon class="ml-1 cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="!formData.params || formData.params.length === 0">
|
||||
<el-button type="success" plain @click="formData.params = [{ key: '', value: '' }]">
|
||||
添加路由参数
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div v-for="(item, index) in formData.params" :key="index">
|
||||
<el-input v-model="item.key" placeholder="参数名" style="width: 100px" />
|
||||
|
||||
<span class="mx-1">=</span>
|
||||
|
||||
<el-input v-model="item.value" placeholder="参数值" style="width: 100px" />
|
||||
|
||||
<el-icon
|
||||
v-if="formData.params.indexOf(item) === formData.params.length - 1"
|
||||
class="ml-2 cursor-pointer color-[var(--el-color-success)]"
|
||||
style="vertical-align: -0.15em"
|
||||
@click="formData.params.push({ key: '', value: '' })"
|
||||
>
|
||||
<CirclePlusFilled />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="ml-2 cursor-pointer color-[var(--el-color-danger)]"
|
||||
style="vertical-align: -0.15em"
|
||||
@click="formData.params.splice(formData.params.indexOf(item), 1)"
|
||||
>
|
||||
<DeleteFilled />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.type !== MenuTypeEnum.BUTTON" prop="hidden" label="显示状态">
|
||||
<el-radio-group v-model="formData.hidden">
|
||||
<el-radio :value="1">显示</el-radio>
|
||||
<el-radio :value="0">隐藏</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="formData.type === MenuTypeEnum.MENU || formData.type === MenuTypeEnum.MENU"
|
||||
>
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
始终显示
|
||||
<el-tooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
选择“是”,即使目录或菜单下只有一个子节点,也会显示父节点。
|
||||
<br />
|
||||
选择“否”,如果目录或菜单下只有一个子节点,则只显示该子节点,隐藏父节点。
|
||||
<br />
|
||||
如果是叶子节点,请选择“否”。
|
||||
</template>
|
||||
<el-icon class="ml-1 cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-radio-group v-model="formData.alwaysShow">
|
||||
<el-radio :value="1">是</el-radio>
|
||||
<el-radio :value="0">否</el-radio>
|
||||
<el-radio :value="1">隐藏</el-radio>
|
||||
<el-radio :value="0">显示</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
@@ -307,8 +252,12 @@
|
||||
</el-form-item>
|
||||
|
||||
<!-- 权限标识 -->
|
||||
<el-form-item v-if="formData.type == MenuTypeEnum.BUTTON" label="权限标识" prop="perm">
|
||||
<el-input v-model="formData.perm" placeholder="sys:user:add" />
|
||||
<el-form-item
|
||||
v-if="formData.type == MenuTypeEnum.BUTTON"
|
||||
label="权限标识"
|
||||
prop="permission"
|
||||
>
|
||||
<el-input v-model="formData.permission" placeholder="sys:user:add" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="formData.type !== MenuTypeEnum.BUTTON" label="图标" prop="icon">
|
||||
@@ -360,7 +309,7 @@ const menuOptions = ref<OptionType[]>([]);
|
||||
}
|
||||
const initialeditRequestData = ref<editRequest>({
|
||||
id: "",
|
||||
pid: 0,
|
||||
pid: "",
|
||||
hidden: 0,
|
||||
menuSort: 1,
|
||||
type: 0, // 默认菜单
|
||||
@@ -376,12 +325,12 @@ const initialeditRequestData = ref<editRequest>({
|
||||
const formData = ref({ ...initialeditRequestData.value });
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
pid: [{ required: true, message: "请选择父级菜单", trigger: "blur" }],
|
||||
pid: [{ required: false, message: "请选择父级菜单", trigger: "blur" }],
|
||||
title: [{ required: true, message: "请输入菜单名称", trigger: "blur" }],
|
||||
type: [{ required: true, message: "请选择菜单类型", trigger: "blur" }],
|
||||
routeName: [{ required: true, message: "请输入路由名称", trigger: "blur" }],
|
||||
routePath: [{ required: true, message: "请输入路由路径", trigger: "blur" }],
|
||||
component: [{ required: true, message: "请输入组件路径", trigger: "blur" }],
|
||||
name: [{ required: false, message: "请输入路由名称", trigger: "blur" }],
|
||||
path: [{ required: true, message: "请输入路由路径", trigger: "blur" }],
|
||||
component: [{ required: false, message: "请输入组件路径", trigger: "blur" }],
|
||||
hidden: [{ required: true, message: "请选择显示状态", trigger: "change" }],
|
||||
});
|
||||
|
||||
@@ -440,10 +389,19 @@ function handleOpenDialog(pid?: string, menuId?: string) {
|
||||
.then(() => {
|
||||
dialog.visible = true;
|
||||
if (menuId) {
|
||||
console.log(menuId);
|
||||
dialog.title = "编辑菜单";
|
||||
MenuAPI.get(menuId).then((data) => {
|
||||
initialeditRequestData.value = { ...data };
|
||||
formData.value = data;
|
||||
console.log(data);
|
||||
initialeditRequestData.value = {
|
||||
...data,
|
||||
};
|
||||
formData.value = {
|
||||
...data,
|
||||
cache: data.cache ? 1 : 0,
|
||||
hidden: data.hidden ? 1 : 0,
|
||||
pid: !pid ? "0" : data.pid,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
dialog.title = "新增菜单";
|
||||
@@ -462,7 +420,7 @@ function handleMenuTypeChange() {
|
||||
formData.value.component = "";
|
||||
} else {
|
||||
// 其他情况,保留原有的组件路径
|
||||
formData.value.routePath = initialeditRequestData.value.routePath;
|
||||
formData.value.path = initialeditRequestData.value.path;
|
||||
formData.value.component = initialeditRequestData.value.component;
|
||||
}
|
||||
}
|
||||
@@ -475,20 +433,26 @@ function handleMenuTypeChange() {
|
||||
function handleSubmit() {
|
||||
editRequestRef.value.validate((isValid: boolean) => {
|
||||
if (isValid) {
|
||||
const menuId = formData.value.id;
|
||||
const menuId = formData.value.menuId;
|
||||
const submitFormData = {
|
||||
...formData.value,
|
||||
cache: formData.value.cache ? 1 : 0,
|
||||
hidden: formData.value.hidden ? 1 : 0,
|
||||
pid: formData.value.pid == 0 ? null : formData.value.pid,
|
||||
};
|
||||
if (menuId) {
|
||||
//修改时父级菜单不能为当前菜单
|
||||
if (formData.value.pid == menuId) {
|
||||
ElMessage.error("父级菜单不能为当前菜单");
|
||||
return;
|
||||
}
|
||||
MenuAPI.edit(menuId, formData.value).then(() => {
|
||||
MenuAPI.edit(menuId, submitFormData).then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
handleCloseDialog();
|
||||
handleQuery();
|
||||
});
|
||||
} else {
|
||||
MenuAPI.add(formData.value).then(() => {
|
||||
MenuAPI.add(submitFormData).then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
handleCloseDialog();
|
||||
handleQuery();
|
||||
@@ -532,7 +496,7 @@ function resetForm() {
|
||||
editRequestRef.value.clearValidate();
|
||||
formData.value = {
|
||||
id: "",
|
||||
pid: 0,
|
||||
pid: "",
|
||||
hidden: 0,
|
||||
menuSort: 1,
|
||||
type: 0, // 默认菜单
|
||||
|
||||
128
src/views/admim/system/role/components/menus.vue
Normal file
128
src/views/admim/system/role/components/menus.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div class="flex-x-between">
|
||||
<el-input v-model="permKeywords" clearable class="w-[150px]" placeholder="菜单菜单名称">
|
||||
<template #prefix>
|
||||
<Search />
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<div class="flex-center ml-5">
|
||||
<el-button type="primary" size="small" plain @click="togglePermTree">
|
||||
<template #icon>
|
||||
<Switch />
|
||||
</template>
|
||||
{{ isExpanded ? "收缩" : "展开" }}
|
||||
</el-button>
|
||||
<el-checkbox v-model="parentChildLinked" class="ml-5" @change="handleparentChildLinkedChange">
|
||||
父子联动
|
||||
</el-checkbox>
|
||||
|
||||
<el-tooltip placement="bottom">
|
||||
<template #content>
|
||||
如果只需勾选菜单菜单,不需要勾选子菜单或者按钮菜单,请关闭父子联动
|
||||
</template>
|
||||
<el-icon class="ml-1 color-[--el-color-primary] inline-block cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-tree
|
||||
ref="permTreeRef"
|
||||
node-key="value"
|
||||
show-checkbox
|
||||
:data="menuPermOptions"
|
||||
:filter-node-method="handlePermFilter"
|
||||
:default-expand-all="true"
|
||||
:check-strictly="!parentChildLinked"
|
||||
class="mt-5"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
{{ data.label }}
|
||||
</template>
|
||||
</el-tree>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MenuAPI from "@/api/account/menu";
|
||||
|
||||
const modelValue = defineModel("modelValue", {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
});
|
||||
const permKeywords = ref("");
|
||||
const isExpanded = ref(true);
|
||||
const parentChildLinked = ref(true);
|
||||
const permTreeRef = ref();
|
||||
// 展开/收缩 菜单菜单树
|
||||
function togglePermTree() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
if (permTreeRef.value) {
|
||||
Object.values(permTreeRef.value.store.nodesMap).forEach((node) => {
|
||||
if (isExpanded.value) {
|
||||
node.expand();
|
||||
} else {
|
||||
node.collapse();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function handlePermFilter(value, data) {
|
||||
if (!value) return true;
|
||||
return data.label.includes(value);
|
||||
}
|
||||
|
||||
// 父子菜单节点是否联动
|
||||
function handleparentChildLinkedChange(val) {
|
||||
parentChildLinked.value = val;
|
||||
}
|
||||
|
||||
// 菜单菜单下拉
|
||||
const menuPermOptions = ref([]);
|
||||
|
||||
function returnMenu(menu) {
|
||||
return menu.map((v) => {
|
||||
return {
|
||||
...v,
|
||||
label: v.title,
|
||||
value: v.menuId,
|
||||
children: v.children ? returnMenu(v.children) : [],
|
||||
};
|
||||
});
|
||||
}
|
||||
// 获取所有的菜单
|
||||
async function getMenuPermOptions() {
|
||||
let arr = await MenuAPI.getList();
|
||||
menuPermOptions.value = returnMenu(arr);
|
||||
}
|
||||
getMenuPermOptions();
|
||||
|
||||
onMounted(() => {
|
||||
console.log(modelValue.value);
|
||||
});
|
||||
|
||||
watch(
|
||||
() => modelValue.value,
|
||||
(newval) => {}
|
||||
);
|
||||
|
||||
function getPerms() {
|
||||
return permTreeRef.value.getCheckedKeys();
|
||||
}
|
||||
function reset() {
|
||||
permTreeRef.value.setCheckedKeys([]);
|
||||
}
|
||||
function setChecked(checkedMenuIds) {
|
||||
checkedMenuIds.forEach((menuId) => {
|
||||
console.log(menuId);
|
||||
permTreeRef.value.setChecked(menuId, true, false);
|
||||
});
|
||||
}
|
||||
defineExpose({
|
||||
getPerms,
|
||||
setChecked,
|
||||
reset,
|
||||
});
|
||||
</script>
|
||||
@@ -35,18 +35,10 @@
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="角色名称" prop="name" min-width="100" />
|
||||
<el-table-column label="角色编码" prop="code" width="150" />
|
||||
|
||||
<el-table-column label="状态" align="center" 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 label="排序" align="center" width="80" prop="sort" />
|
||||
|
||||
<el-table-column label="角色名称" prop="name" />
|
||||
<el-table-column label="角色级别" prop="level" />
|
||||
<el-table-column label="描述" prop="description" />
|
||||
<el-table-column label="创建日期" prop="createTime" />
|
||||
<el-table-column fixed="right" label="操作" width="220">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
@@ -56,7 +48,7 @@
|
||||
icon="position"
|
||||
@click="handleOpenAssignPermDialog(scope.row)"
|
||||
>
|
||||
分配权限
|
||||
分配菜单
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@@ -101,33 +93,24 @@
|
||||
<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 label="角色等级" prop="level">
|
||||
<el-input-number placeholder="角色等级" v-model="formData.level"></el-input-number>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="数据权限" prop="dataScope">
|
||||
<!-- <el-form-item label="数据菜单" prop="dataScope">
|
||||
<el-select v-model="formData.dataScope">
|
||||
<el-option :key="0" label="全部数据" :value="0" />
|
||||
<el-option :key="1" label="部门及子部门数据" :value="1" />
|
||||
<el-option :key="2" label="本部门数据" :value="2" />
|
||||
<el-option :key="3" label="本人数据" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input type="textarea" v-model="formData.description" placeholder="描述" />
|
||||
</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="sort">
|
||||
<el-input-number
|
||||
v-model="formData.sort"
|
||||
controls-position="right"
|
||||
:min="0"
|
||||
style="width: 100px"
|
||||
/>
|
||||
<el-form-item label="菜单分配" prop="menuIdList">
|
||||
<menuSelect ref="refmenuSelect" v-model="formData.menuIdList"></menuSelect>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -139,14 +122,14 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 分配权限弹窗 -->
|
||||
<!-- 分配菜单弹窗 -->
|
||||
<el-drawer
|
||||
v-model="assignPermDialogVisible"
|
||||
:title="'【' + checkedRole.name + '】权限分配'"
|
||||
:title="'【' + checkedRole.name + '】菜单分配'"
|
||||
size="500"
|
||||
>
|
||||
<div class="flex-x-between">
|
||||
<el-input v-model="permKeywords" clearable class="w-[150px]" placeholder="菜单权限名称">
|
||||
<el-input v-model="permKeywords" clearable class="w-[150px]" placeholder="菜单菜单名称">
|
||||
<template #prefix>
|
||||
<Search />
|
||||
</template>
|
||||
@@ -169,7 +152,7 @@
|
||||
|
||||
<el-tooltip placement="bottom">
|
||||
<template #content>
|
||||
如果只需勾选菜单权限,不需要勾选子菜单或者按钮权限,请关闭父子联动
|
||||
如果只需勾选菜单菜单,不需要勾选子菜单或者按钮菜单,请关闭父子联动
|
||||
</template>
|
||||
<el-icon class="ml-1 color-[--el-color-primary] inline-block cursor-pointer">
|
||||
<QuestionFilled />
|
||||
@@ -207,12 +190,13 @@ defineOptions({
|
||||
name: "Role",
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import menuSelect from "./components/menus.vue";
|
||||
import RoleApi, { SysRole, addRequest, getListRequest } from "@/api/account/role";
|
||||
import MenuAPI, { type RouteVO } from "@/api/account/menu";
|
||||
|
||||
const queryFormRef = ref();
|
||||
const addRequestRef = ref();
|
||||
const refmenuSelect = ref();
|
||||
const permTreeRef = ref();
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -227,7 +211,7 @@ const queryParams = reactive<getListRequest>({
|
||||
|
||||
// 角色表格数据
|
||||
const roleList = ref<SysRole[]>();
|
||||
// 菜单权限下拉
|
||||
// 菜单菜单下拉
|
||||
const menuPermOptions = ref<RouteVO[]>([]);
|
||||
|
||||
// 弹窗
|
||||
@@ -248,9 +232,7 @@ const formData = reactive<addRequest>({
|
||||
|
||||
const rules = reactive({
|
||||
name: [{ required: true, message: "请输入角色名称", trigger: "blur" }],
|
||||
code: [{ required: true, message: "请输入角色编码", trigger: "blur" }],
|
||||
dataScope: [{ required: true, message: "请选择数据权限", trigger: "blur" }],
|
||||
status: [{ required: true, message: "请选择状态", trigger: "blur" }],
|
||||
level: [{ required: false, message: "请输入角色等级", trigger: "blur" }],
|
||||
});
|
||||
|
||||
// 选中的角色
|
||||
@@ -294,11 +276,19 @@ function handleSelectionChange(selection: any) {
|
||||
}
|
||||
|
||||
// 打开角色弹窗
|
||||
function handleOpenDialog(row: SysRole) {
|
||||
async function handleOpenDialog(row: SysRole) {
|
||||
dialog.visible = true;
|
||||
if (row.id) {
|
||||
if (row && row.id) {
|
||||
dialog.title = "修改角色";
|
||||
//获取角色菜单列表
|
||||
const data = await RoleApi.getMenu(row.id);
|
||||
Object.assign(formData, row);
|
||||
console.log(data);
|
||||
formData.menuIdList = data;
|
||||
setTimeout(() => {
|
||||
refmenuSelect.value.setChecked(data);
|
||||
}, 100);
|
||||
console.log(formData);
|
||||
} else {
|
||||
dialog.title = "新增角色";
|
||||
}
|
||||
@@ -308,10 +298,11 @@ function handleOpenDialog(row: SysRole) {
|
||||
function handleSubmit() {
|
||||
addRequestRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const checkedMenuIds: number[] = refmenuSelect.value.getPerms();
|
||||
loading.value = true;
|
||||
const roleId = formData.id;
|
||||
if (roleId) {
|
||||
RoleApi.update({ ...formData, id: formData.id ?? null })
|
||||
RoleApi.update(roleId, { ...formData, menuIdList: checkedMenuIds })
|
||||
.then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
handleCloseDialog();
|
||||
@@ -320,7 +311,8 @@ function handleSubmit() {
|
||||
.finally(() => (loading.value = false));
|
||||
} else {
|
||||
delete formData.id;
|
||||
RoleApi.add(formData)
|
||||
|
||||
RoleApi.add({ ...formData, menuIdList: checkedMenuIds })
|
||||
.then(() => {
|
||||
ElMessage.success("新增成功");
|
||||
handleCloseDialog();
|
||||
@@ -338,7 +330,7 @@ function handleCloseDialog() {
|
||||
|
||||
addRequestRef.value.resetFields();
|
||||
addRequestRef.value.clearValidate();
|
||||
|
||||
refmenuSelect.value.reset();
|
||||
formData.id = undefined;
|
||||
formData.sort = 1;
|
||||
formData.status = 1;
|
||||
@@ -378,7 +370,7 @@ interface typeRoute extends RouteVO {
|
||||
children: typeRoute[];
|
||||
}
|
||||
|
||||
function returnMenu(menu: RouteVO[]): typeRoute[] {
|
||||
function returnMenu(menu) {
|
||||
return menu.map((v) => {
|
||||
return {
|
||||
...v,
|
||||
@@ -389,7 +381,13 @@ function returnMenu(menu: RouteVO[]): typeRoute[] {
|
||||
});
|
||||
}
|
||||
|
||||
// 打开分配菜单权限弹窗
|
||||
// 获取所有的菜单
|
||||
async function getMenuPermOptions() {
|
||||
let arr = await MenuAPI.getList({});
|
||||
menuPermOptions.value = returnMenu(arr);
|
||||
}
|
||||
getMenuPermOptions();
|
||||
// 打开分配菜单菜单弹窗
|
||||
async function handleOpenAssignPermDialog(row: SysRole) {
|
||||
const roleId = row.id;
|
||||
if (roleId) {
|
||||
@@ -399,34 +397,31 @@ async function handleOpenAssignPermDialog(row: SysRole) {
|
||||
checkedRole.value.id = roleId;
|
||||
checkedRole.value.name = row.name as string;
|
||||
|
||||
// 获取所有的菜单
|
||||
let arr: RouteVO[] = await MenuAPI.getRoutes();
|
||||
menuPermOptions.value = returnMenu(arr);
|
||||
|
||||
// 回显角色已拥有的菜单
|
||||
// RoleApi.getRoleMenuIds(roleId)
|
||||
// .then((data) => {
|
||||
// const checkedMenuIds = data;
|
||||
// checkedMenuIds.forEach((menuId) => permTreeRef.value!.setChecked(menuId, true, false));
|
||||
// })
|
||||
// .finally(() => {
|
||||
// loading.value = false;
|
||||
// });
|
||||
RoleApi.getMenu(roleId)
|
||||
.then((data) => {
|
||||
const checkedMenuIds = data;
|
||||
checkedMenuIds.forEach((menuId) => permTreeRef.value!.setChecked(menuId, true, false));
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 分配菜单权限提交
|
||||
// 分配菜单菜单提交
|
||||
function handleAssignPermSubmit() {
|
||||
const roleId = checkedRole.value.id;
|
||||
const name = checkedRole.value.name;
|
||||
if (roleId) {
|
||||
const checkedMenuIds: number[] = permTreeRef
|
||||
.value!.getCheckedNodes(false, true)
|
||||
.map((node: any) => node.value);
|
||||
|
||||
loading.value = true;
|
||||
RoleApi.updateRoleMenus(roleId, checkedMenuIds)
|
||||
RoleApi.update(roleId, { name, menuIdList: checkedMenuIds })
|
||||
.then(() => {
|
||||
ElMessage.success("分配权限成功");
|
||||
ElMessage.success("分配菜单成功");
|
||||
assignPermDialogVisible.value = false;
|
||||
handleResetQuery();
|
||||
})
|
||||
@@ -436,7 +431,7 @@ function handleAssignPermSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
// 展开/收缩 菜单权限树
|
||||
// 展开/收缩 菜单菜单树
|
||||
function togglePermTree() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
if (permTreeRef.value) {
|
||||
@@ -449,8 +444,20 @@ function togglePermTree() {
|
||||
});
|
||||
}
|
||||
}
|
||||
function editTogglePermTree() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
if (permTreeRef.value) {
|
||||
Object.values(permTreeRef.value.store.nodesMap).forEach((node: any) => {
|
||||
if (isExpanded.value) {
|
||||
node.expand();
|
||||
} else {
|
||||
node.collapse();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 权限筛选
|
||||
// 菜单筛选
|
||||
watch(permKeywords, (val) => {
|
||||
permTreeRef.value!.filter(val);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<template>
|
||||
<div class="login" :style="'background-image:url(' + Background + ');'">
|
||||
<el-form ref="loginForm" :model="state.loginForm" :rules="state.loginRules" label-position="left" label-width="0px"
|
||||
class="login-form">
|
||||
<el-form
|
||||
ref="loginForm"
|
||||
:model="state.loginForm"
|
||||
:rules="state.loginRules"
|
||||
label-position="left"
|
||||
label-width="0px"
|
||||
class="login-form"
|
||||
>
|
||||
<h3 class="title">银收客后台管理</h3>
|
||||
<el-form-item>
|
||||
<el-radio-group v-model="state.loginForm.loginType">
|
||||
@@ -10,19 +16,39 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="merchantName" v-if="state.loginForm.loginType == 'staff'">
|
||||
<el-input v-model="state.loginForm.merchantName" type="text" auto-complete="off" placeholder="商户号"></el-input>
|
||||
<el-input
|
||||
v-model="state.loginForm.merchantName"
|
||||
type="text"
|
||||
auto-complete="off"
|
||||
placeholder="商户号"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="state.loginForm.username" type="text" auto-complete="off" placeholder="账号"></el-input>
|
||||
<el-input
|
||||
v-model="state.loginForm.username"
|
||||
type="text"
|
||||
auto-complete="off"
|
||||
placeholder="账号"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input v-model="state.loginForm.password" type="password" auto-complete="off" placeholder="密码"
|
||||
@keyup.enter="handleLogin"></el-input>
|
||||
<el-input
|
||||
v-model="state.loginForm.password"
|
||||
type="password"
|
||||
auto-complete="off"
|
||||
placeholder="密码"
|
||||
@keyup.enter="handleLogin"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<div class="code_wrap">
|
||||
<el-input v-model="state.loginForm.code" auto-complete="off" placeholder="验证码" style="width: 63%"
|
||||
@keyup.enter="handleLogin"></el-input>
|
||||
<el-input
|
||||
v-model="state.loginForm.code"
|
||||
auto-complete="off"
|
||||
placeholder="验证码"
|
||||
style="width: 63%"
|
||||
@keyup.enter="handleLogin"
|
||||
></el-input>
|
||||
<div class="login-code">
|
||||
<img :src="state.codeUrl" @click="getCode" />
|
||||
</div>
|
||||
@@ -30,8 +56,13 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item style="width: 100%">
|
||||
<el-button :loading="state.loading" size="default" type="primary" style="width: 100%"
|
||||
@click.prevent="handleLogin">
|
||||
<el-button
|
||||
:loading="state.loading"
|
||||
size="default"
|
||||
type="primary"
|
||||
style="width: 100%"
|
||||
@click.prevent="handleLogin"
|
||||
>
|
||||
<span v-if="!state.loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
</el-button>
|
||||
@@ -61,8 +92,8 @@ const state = reactive({
|
||||
codeUrl: "",
|
||||
cookiePass: "",
|
||||
loginForm: {
|
||||
username: "",
|
||||
password: "",
|
||||
username: "admin",
|
||||
password: "12345",
|
||||
// rememberMe: false,
|
||||
code: "",
|
||||
uuid: "",
|
||||
@@ -152,7 +183,7 @@ function handleLogin() {
|
||||
.then(async (res) => {
|
||||
// await userStore.getUserInfo();
|
||||
const { path, queryParams } = parseRedirect();
|
||||
console.log(res, 'Denglv返回');
|
||||
console.log(res, "Denglv返回");
|
||||
router.push({ path: path, query: queryParams });
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="聚合支付" name="pay">
|
||||
<el-form ref="form" :model="form" label-width="120px" label-position="left">
|
||||
<el-form-item label="店铺id">
|
||||
<el-input v-model="form.storeId" placeholder="请输入店铺id"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="商户号">
|
||||
<el-input v-model="form.appId" placeholder="请输入商户号"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="商户密钥">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="form.appToken"
|
||||
v-model="form.appSecret"
|
||||
placeholder="请输入商户密钥"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
@@ -17,7 +20,10 @@
|
||||
<el-input v-model="form.payPassword" placeholder="请输入支付密码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="微信appid">
|
||||
<el-input v-model="form.smallAppid" placeholder="请输入微信小程序appid"></el-input>
|
||||
<el-input
|
||||
v-model="form.wechatSmallAppid"
|
||||
placeholder="请输入微信小程序appid"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付宝appid">
|
||||
<el-input
|
||||
@@ -31,32 +37,33 @@
|
||||
<el-form-item label="支付宝商户密钥">
|
||||
<el-input v-model="form.alipayAppToken" placeholder="请输入支付宝商户密钥"></el-input>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="店铺id">
|
||||
<el-input v-model="form.storeId" placeholder="请输入店铺id"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio :label="1">启用</el-radio>
|
||||
<el-radio :label="-1">禁用</el-radio>
|
||||
<el-radio :value="1">启用</el-radio>
|
||||
<el-radio :value="-1">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="submitHandle" :loading="formLoading">
|
||||
<span v-if="!formLoading">保存</span>
|
||||
<span v-else>保存中...</span>
|
||||
</el-button>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="submitHandle" :loading="formLoading">
|
||||
<span v-if="!formLoading">保存</span>
|
||||
<span v-else>保存中...</span>
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { tbMerchantThirdApply, tbMerchantThirdApplyPut } from "@/api/shop";
|
||||
import ShopApi from "@/api/account/shop";
|
||||
|
||||
import shopMerchantApi from "@/api/account/shopMerchant";
|
||||
import { ElNotification } from "element-plus";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -64,12 +71,12 @@ export default {
|
||||
activeName: "pay",
|
||||
formLoading: false,
|
||||
form: {
|
||||
appToken: "",
|
||||
appSecret: "",
|
||||
id: "",
|
||||
payPassword: "",
|
||||
status: 1,
|
||||
appId: "",
|
||||
smallAppid: "",
|
||||
wechatSmallAppid: "",
|
||||
storeId: "",
|
||||
alipaySmallAppid: "",
|
||||
alipayAppToken: "",
|
||||
@@ -82,12 +89,12 @@ export default {
|
||||
async submitHandle() {
|
||||
this.formLoading = true;
|
||||
try {
|
||||
await tbMerchantThirdApplyPut(this.form);
|
||||
await shopMerchantApi.edit(this.form.id, this.form);
|
||||
this.$emit("success");
|
||||
this.formLoading = false;
|
||||
this.$notify({
|
||||
ElNotification({
|
||||
title: "成功",
|
||||
message: `提交成功`,
|
||||
message: `修改成功`,
|
||||
type: "success",
|
||||
});
|
||||
this.close();
|
||||
@@ -100,7 +107,7 @@ export default {
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
reset() {
|
||||
this.form.appToken = "";
|
||||
this.form.appSecret = "";
|
||||
this.form.id = "";
|
||||
this.form.payPassword = "";
|
||||
this.form.status = 1;
|
||||
@@ -108,14 +115,16 @@ export default {
|
||||
},
|
||||
// 详情(配置三方支付)
|
||||
async getDetail(id) {
|
||||
console.log(id);
|
||||
try {
|
||||
const res = await tbMerchantThirdApply(id);
|
||||
this.form.appToken = res.appToken;
|
||||
const res = await shopMerchantApi.get(id);
|
||||
this.form.appSecret = res.appSecret;
|
||||
this.form.payPassword = res.payPassword;
|
||||
this.form.status = res.status;
|
||||
this.form.appId = res.appId;
|
||||
this.form.smallAppid = res.smallAppid;
|
||||
this.form.wechatSmallAppid = res.wechatSmallAppid;
|
||||
this.form.alipaySmallAppid = res.alipaySmallAppid;
|
||||
this.form.merchantName = res.merchantName;
|
||||
//this.form.alipayAppToken = res.alipayAppToken
|
||||
//this.form.alipayAppId = res.alipayAppId
|
||||
this.form.storeId = res.storeId;
|
||||
@@ -126,8 +135,8 @@ export default {
|
||||
},
|
||||
show(obj) {
|
||||
if (obj && obj.id) {
|
||||
this.form.id = obj.merchantId;
|
||||
this.getDetail(obj.merchantId);
|
||||
this.form.id = obj.id;
|
||||
this.getDetail(obj.id);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -184,12 +184,13 @@ onMounted(() => {
|
||||
|
||||
const refDetailModal = ref(null);
|
||||
function dropdownClick(e) {
|
||||
switch (e.command) {
|
||||
case 1:
|
||||
refDetailModal.value.show(e.row);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
console.log(e);
|
||||
if (e.command == 1) {
|
||||
refDetailModal.value.show(e.row);
|
||||
return;
|
||||
}
|
||||
if (e == 5) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 重置查询
|
||||
|
||||
1
src/views/shop/staff/components/select-permission.vue
Normal file
1
src/views/shop/staff/components/select-permission.vue
Normal file
@@ -0,0 +1 @@
|
||||
<template>11</template>
|
||||
@@ -43,9 +43,8 @@
|
||||
|
||||
<!-- 新增 -->
|
||||
<page-modal ref="addModalRef" :modal-config="addModalConfig" @submit-click="handleSubmitClick">
|
||||
<template #url="scope">
|
||||
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
|
||||
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
|
||||
<template #permission="scope">
|
||||
<selectPermission></selectPermission>
|
||||
</template>
|
||||
</page-modal>
|
||||
|
||||
@@ -55,9 +54,8 @@
|
||||
:modal-config="editModalConfig"
|
||||
@submit-click="handleSubmitClick"
|
||||
>
|
||||
<template #url="scope">
|
||||
<FileUpload v-model="scope.formData[scope.prop]" :limit="1" v-bind="scope.attrs" />
|
||||
<!-- <Dict v-model="scope.formData[scope.prop]" code="gender" v-bind="scope.attrs" /> -->
|
||||
<template #permission="scope">
|
||||
<selectPermission></selectPermission>
|
||||
</template>
|
||||
</page-modal>
|
||||
</div>
|
||||
@@ -73,7 +71,9 @@ import searchConfig from "./config/search";
|
||||
import { returnOptionsLabel } from "./config/config";
|
||||
import RoleApi, { type SysRole } from "@/api/account/role";
|
||||
import ShopStaffApi from "@/api/account/shopStaff";
|
||||
|
||||
import permissionApi from "@/api/account/permission";
|
||||
import selectPermission from "./components/select-permission.vue";
|
||||
permissionApi.getshopPermission();
|
||||
const {
|
||||
searchRef,
|
||||
contentRef,
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">11</div>
|
||||
</template>
|
||||
73
src/views/tool/table/components/addEara.vue
Normal file
73
src/views/tool/table/components/addEara.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑区域' : '添加区域'" :visible.sync="dialogVisible" @close="reset">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
|
||||
<el-form-item label="区域名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入区域名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmitHandle">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { tbShopArea } from '@/api/table'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
form: {
|
||||
id: '',
|
||||
name: ''
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入区域名称',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSubmitHandle() {
|
||||
this.$refs.form.validate(async valid => {
|
||||
if (valid) {
|
||||
try {
|
||||
let res = await tbShopArea({
|
||||
...this.form,
|
||||
shopId: localStorage.getItem('shopId')
|
||||
}, this.form.id ? 'put' : 'post')
|
||||
this.$emit('success', res)
|
||||
this.close()
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: `${this.form.id ? '编辑' : '添加'}成功`,
|
||||
type: 'success'
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
show(obj) {
|
||||
this.dialogVisible = true
|
||||
if (obj && obj.id) {
|
||||
this.form = JSON.parse(JSON.stringify(obj))
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.dialogVisible = false
|
||||
},
|
||||
reset() {
|
||||
this.form.id = ''
|
||||
this.form.name = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
235
src/views/tool/table/components/addTable.vue
Normal file
235
src/views/tool/table/components/addTable.vue
Normal file
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:title="form.id ? '编辑台桌' : '添加台桌'"
|
||||
:visible.sync="dialogVisible"
|
||||
@open="tbShopAreaGet"
|
||||
@close="reset"
|
||||
>
|
||||
<el-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="left"
|
||||
>
|
||||
<el-form-item label="选择区域" prop="areaId">
|
||||
<el-select v-model="form.areaId" placeholder="请选择区域"
|
||||
@change="selectChange($event, 'form' , 'areaId')"
|
||||
>
|
||||
<el-option
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
v-for="item in areaList"
|
||||
:key="item.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="台桌状态" prop="status" v-if="form.id">
|
||||
<el-select v-model="form.status" placeholder="请选择台桌状态">
|
||||
<el-option
|
||||
:label="item.name"
|
||||
:value="item.value"
|
||||
v-for="item in status"
|
||||
:key="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="台桌标识" prop="sign">
|
||||
<div class="u-flex">
|
||||
<div class="u-flex" style="width: 57px;">
|
||||
<el-input
|
||||
v-model="form.sign"
|
||||
placeholder="A"
|
||||
></el-input>
|
||||
</div>
|
||||
|
||||
<div class="u-flex u-m-l-30" >
|
||||
<div class="u-flex">
|
||||
<div class="u-m-r-4">起始</div>
|
||||
<el-input v-model="form.start" style="width: 57px;" placeholder="请输入起始值" >
|
||||
</el-input>
|
||||
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="
|
||||
background-color: #D9D9D9;
|
||||
height: 1px;
|
||||
width: 32px;
|
||||
border-radius: 1px;
|
||||
"
|
||||
class="u-m-l-16 u-m-r-16"
|
||||
></div>
|
||||
<div class="u-flex">
|
||||
<span class="u-m-r-4">结束</span>
|
||||
<el-input v-model="form.end" style="width: 57px;" placeholder="请输入结束值">
|
||||
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="客座数">
|
||||
<el-input-number
|
||||
v-model="form.capacity"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="清台管理">
|
||||
<el-radio-group v-model="form.autoClear">
|
||||
<el-radio-button :label="0">手动清台</el-radio-button>
|
||||
<el-radio-button :label="1">自动清台</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="网络预定开关">
|
||||
<el-switch
|
||||
v-model="form.isPredate"
|
||||
:active-value="1"
|
||||
:inactive-value="2"
|
||||
></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button :label="0">低消</el-radio-button>
|
||||
<el-radio-button :label="2">计时</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="最低消费" v-if="form.type == 0">
|
||||
<el-input-number
|
||||
v-model="form.amount"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="每小时收费" v-if="form.type == 2">
|
||||
<el-input-number
|
||||
v-model="form.perhour"
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="onSubmitHandle"
|
||||
>确 定</el-button
|
||||
>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { tbShopTable, tbShopAreaGet ,$fastCreateTable} from "@/api/table";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
resetForm: "",
|
||||
loading: false,
|
||||
status: [
|
||||
{ value: "pending", name: "挂单中" },
|
||||
{ value: "using", name: "开台中" },
|
||||
{ value: "paying", name: "结算中" },
|
||||
{ value: "idle", name: "空闲" },
|
||||
{ value: "subscribe", name: "预定" },
|
||||
{ value: "closed", name: "关台" },
|
||||
// {value:'opening',name:'开台中'},
|
||||
{ value: "cleaning", name: "台桌清理中" },
|
||||
],
|
||||
form: {
|
||||
id: "",
|
||||
areaId: "",
|
||||
sign:'',
|
||||
start:1,
|
||||
end:10,
|
||||
capacity: 0,
|
||||
isPredate: 1,
|
||||
type: 2,
|
||||
perhour: 0,
|
||||
amount: 0,
|
||||
autoClear: 1,
|
||||
},
|
||||
rules: {
|
||||
areaId: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择区域",
|
||||
trigger: ["blur","change"],
|
||||
},
|
||||
],
|
||||
sign: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入台桌标识",
|
||||
trigger: ["blur","change"],
|
||||
},
|
||||
],
|
||||
},
|
||||
areaList: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.resetForm = { ...this.form };
|
||||
},
|
||||
methods: {
|
||||
//解决selectc值改变后未验证问题
|
||||
selectChange($event, ref, type) {
|
||||
this.$refs[ref][0].validateField(type)
|
||||
},
|
||||
onSubmitHandle() {
|
||||
this.$refs.form.validate(async (valid) => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
try {
|
||||
let res = await $fastCreateTable(
|
||||
{
|
||||
...this.form,
|
||||
qrcode: this.form.tableId,
|
||||
shopId: localStorage.getItem("shopId"),
|
||||
},
|
||||
this.form.id ? "put" : "post"
|
||||
);
|
||||
this.$emit("success", res);
|
||||
this.close();
|
||||
this.$notify({
|
||||
title: "成功",
|
||||
message: `${this.form.id ? "编辑" : "添加"}成功`,
|
||||
type: "success",
|
||||
});
|
||||
this.loading = false;
|
||||
} catch (error) {
|
||||
this.loading = false;
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
show(obj) {
|
||||
this.dialogVisible = true;
|
||||
if (obj && obj.id) {
|
||||
this.form = JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.dialogVisible = false;
|
||||
},
|
||||
reset() {
|
||||
this.form = { ...this.resetForm };
|
||||
},
|
||||
// 获取区域
|
||||
async tbShopAreaGet() {
|
||||
try {
|
||||
const { content } = await tbShopAreaGet({
|
||||
shopId: localStorage.getItem("shopId"),
|
||||
});
|
||||
this.areaList = content;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
270
src/views/tool/table/components/choose-diners-number.vue
Normal file
270
src/views/tool/table/components/choose-diners-number.vue
Normal file
@@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<div class="select_desk">
|
||||
<el-dialog width="410px" title="就餐人数" :visible.sync="show">
|
||||
<div class="select_desk_dialog u-p-b-20">
|
||||
<key-board
|
||||
isCanEmpty
|
||||
v-model="number"
|
||||
@clear="clear"
|
||||
:max="max"
|
||||
:maxTips=" '最多'+max+'位'"
|
||||
>
|
||||
<div slot="clear">清空</div>
|
||||
<div slot="input" class="u-p-l-20 u-p-r-20 u-flex w-full">
|
||||
<el-input
|
||||
placeholder="请输入就餐人数"
|
||||
v-model="number"
|
||||
@input="inputNumber"
|
||||
@change="inputChange"
|
||||
type="number"
|
||||
>
|
||||
<template slot="append">位</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</key-board>
|
||||
<div class="confirm_btns">
|
||||
<el-button size="medium" @click="close">取消</el-button>
|
||||
<el-button type="primary" size="medium" @click="confirm"
|
||||
>确定</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import keyBoard from "../keyboard.vue";
|
||||
export default {
|
||||
components: { keyBoard },
|
||||
props:{
|
||||
max:{
|
||||
type:Number,
|
||||
default:99
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
number: "",
|
||||
show: false,
|
||||
hasOpen: false,
|
||||
loading: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
number(newval) {
|
||||
if (newval >this.max) {
|
||||
this.number = this.max;
|
||||
this.$message("最多只能选择"+this.max+"位就餐人数");
|
||||
}
|
||||
// 使用正则表达式匹配正整数
|
||||
const regex = /^[1-9]\d*$/;
|
||||
// 如果输入的值不是正整数,则将其设置为上一个有效值
|
||||
if (!regex.test(newval)) {
|
||||
this.number = newval.substring(0, newval.length - 1);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
inputNumber(e) {},
|
||||
inputChange(e) {},
|
||||
clear(e) {
|
||||
console.log(e);
|
||||
this.number = "";
|
||||
},
|
||||
confirm() {
|
||||
if (this.number >this.max) {
|
||||
return this.$message("最多只能选择"+this.max+"位就餐人数");
|
||||
}
|
||||
if (!this.number) {
|
||||
return this.$message("请选择就餐人数");
|
||||
}
|
||||
console.log(this.number)
|
||||
this.$emit("confirm", this.number);
|
||||
this.close();
|
||||
},
|
||||
open(number) {
|
||||
this.number = number || "";
|
||||
this.show = true;
|
||||
},
|
||||
close() {
|
||||
this.show = false;
|
||||
this.number = "";
|
||||
},
|
||||
},
|
||||
mounted() {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
::v-deep.el-button {
|
||||
padding: 12px 20px;
|
||||
}
|
||||
::v-deep .el-input__inner::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
::v-deep .el-input__inner::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
::v-deep .el-button--success {
|
||||
border-color: #22bf64;
|
||||
background-color: #22bf64;
|
||||
}
|
||||
.select_desk .btn {
|
||||
height: 34px;
|
||||
}
|
||||
.tags {
|
||||
font-size: 16px;
|
||||
&.using {
|
||||
color: rgb(234, 64, 37);
|
||||
}
|
||||
&.wait {
|
||||
color: rgb(252, 236, 79);
|
||||
}
|
||||
&.idle {
|
||||
color: rgb(137, 234, 71);
|
||||
}
|
||||
&.closed {
|
||||
color: rgb(221, 221, 221);
|
||||
filter: grayscale(1);
|
||||
}
|
||||
}
|
||||
::v-deep .inputs .el-input__inner {
|
||||
border-color: transparent !important;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
letter-spacing: 1.25px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.select_desk .select_desk_dialog {
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .nav {
|
||||
width: 286px;
|
||||
height: 38px;
|
||||
background: #dcf0e8;
|
||||
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .nav .li,
|
||||
.select_desk .select_desk_dialog .nav {
|
||||
border-radius: 4px;
|
||||
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .nav .li {
|
||||
width: 140px;
|
||||
height: 34px;
|
||||
color: #0fc161;
|
||||
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .nav .lion {
|
||||
background: #0fc161;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .inputs {
|
||||
width: 370px;
|
||||
line-height: 54px;
|
||||
margin-top: 24px;
|
||||
height: 54px;
|
||||
margin-bottom: 20px;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
letter-spacing: 1.25px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .inputs .close {
|
||||
color: #aaa;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
line-height: 30px;
|
||||
top: 50%;
|
||||
margin-top: -15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .keyboard {
|
||||
display: flex;
|
||||
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
border-right: 1px solid #dcdfe6;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .keyboard .li {
|
||||
height: 60px;
|
||||
width: 33.333%;
|
||||
|
||||
display: flex;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
color: #212121;
|
||||
cursor: pointer;
|
||||
|
||||
user-select: none;
|
||||
border-left: 1px solid #dcdfe6;
|
||||
border-top: 1px solid #dcdfe6;
|
||||
|
||||
transition: all 0.1s;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .keyboard .li:hover {
|
||||
background: #dcdfe6;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .keyboard .li .icon {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .keyboard .confirm {
|
||||
height: 140px;
|
||||
background: #ff9f2e;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.select_desk .select_desk_dialog .keyboard .confirm:hover {
|
||||
background: #f88502;
|
||||
}
|
||||
|
||||
.confirm_btns {
|
||||
display: flex;
|
||||
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.confirm_btns .el-button {
|
||||
width: 175px;
|
||||
}
|
||||
</style>
|
||||
289
src/views/tool/table/components/choose-user.vue
Normal file
289
src/views/tool/table/components/choose-user.vue
Normal file
@@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<el-dialog title="选择用户" width="850px" :visible.sync="show">
|
||||
<div class="app-container">
|
||||
<div class="head-container">
|
||||
<el-form :model="query" inline>
|
||||
<el-form-item label="">
|
||||
<el-input
|
||||
v-model="query.name"
|
||||
placeholder="请输入昵称或手机号"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="是否为会员">
|
||||
<el-select v-model="query.isVip" placeholder="是否是会员">
|
||||
<el-option value="" label="全部"></el-option>
|
||||
<el-option :value="1" label="是"></el-option>
|
||||
<el-option :value="0" label="否"></el-option>
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<div class="flex gap-20">
|
||||
<el-button type="primary" @click="getTableData" size="medium"
|
||||
>搜索</el-button
|
||||
>
|
||||
<!-- <el-button @click="resetHandle" size="medium">重置</el-button> -->
|
||||
<el-button @click="noChooseUser" size="medium"
|
||||
>不选择用户</el-button
|
||||
>
|
||||
<!-- <el-button @click="resetHandle" size="medium">新建用户</el-button> -->
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<!-- <div class="head-container">
|
||||
<el-button type="primary" icon="el-icon-plus" @click="$refs.addActive.show()">
|
||||
添加活动
|
||||
</el-button>
|
||||
</div> -->
|
||||
|
||||
<div class="head-container">
|
||||
<el-table :data="tableData.data" v-loading="tableData.loading" @cell-click="cellClick">
|
||||
<el-table-column label="ID" prop="id"></el-table-column>
|
||||
<el-table-column label="用户" prop="headImg" width="200px">
|
||||
<template v-slot="scope">
|
||||
<div class="user_info">
|
||||
<el-image
|
||||
:src="scope.row.headImg"
|
||||
style="width: 40px; height: 40px; flex-shrink: 0"
|
||||
>
|
||||
<div slot="error" class="image-slot">
|
||||
<i class="el-icon-user"></i>
|
||||
</div>
|
||||
</el-image>
|
||||
<span class="name">{{ scope.row.nickName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="手机号"
|
||||
prop="telephone"
|
||||
width="160"
|
||||
></el-table-column>
|
||||
<!-- <el-table-column label="性别" prop="sex">
|
||||
<template v-slot="scope">
|
||||
<el-tag type="priamry">{{ scope.row.sex || "未知" }}</el-tag>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="会员" prop="isVip">
|
||||
<template v-slot="scope">
|
||||
<el-tag type="warning" v-if="scope.row.isVip"
|
||||
>会员等级{{ scope.row.isVip }}</el-tag
|
||||
>
|
||||
<span v-else>否</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="余额" prop="amount"></el-table-column>
|
||||
<el-table-column label="积分" prop="totalScore"></el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="90" fixed="right">
|
||||
<template v-slot="scope">
|
||||
<el-button type="primary" size="mini" @click="choose(scope.row)">选择</el-button>
|
||||
<!-- <el-button type="text" @click="charge(scope.row)">充值</el-button> -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="head-container">
|
||||
<el-pagination
|
||||
:total="tableData.total"
|
||||
:current-page="tableData.page + 1"
|
||||
:page-size="tableData.size"
|
||||
@size-change="sizeChange"
|
||||
@current-change="paginationChange"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { queryAllShopUser, queryAllShopInfo } from "@/api/shop";
|
||||
import dayjs from "dayjs";
|
||||
let cacheData = {};
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
query: {
|
||||
name: "",
|
||||
},
|
||||
shopInfo: {
|
||||
balanceTotal: 0,
|
||||
userTotal: 0,
|
||||
chageTotal: 0,
|
||||
},
|
||||
tableData: {
|
||||
data: [],
|
||||
page: 0,
|
||||
size: 10,
|
||||
loading: false,
|
||||
total: 0,
|
||||
},
|
||||
};
|
||||
},
|
||||
filters: {
|
||||
timeFilter(s) {
|
||||
return dayjs(s).format("YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// this.getTableData();
|
||||
},
|
||||
methods: {
|
||||
cellClick(user){
|
||||
console.log(user)
|
||||
this.$emit("chooseUser",user)
|
||||
this.close()
|
||||
},
|
||||
noChooseUser(){
|
||||
this.$emit("chooseUser",null)
|
||||
this.close()
|
||||
|
||||
},
|
||||
choose(user) {
|
||||
this.$emit("chooseUser",user)
|
||||
this.close()
|
||||
|
||||
},
|
||||
charge(user) {
|
||||
console.log(user);
|
||||
},
|
||||
close(){
|
||||
this.show = false;
|
||||
},
|
||||
open() {
|
||||
this.getTableData();
|
||||
this.show = true;
|
||||
},
|
||||
toPage(type) {
|
||||
const pages = {
|
||||
charge: "charge_list",
|
||||
cost: "cost_list",
|
||||
};
|
||||
this.$router.push({
|
||||
name: pages[type],
|
||||
});
|
||||
console.log(pages[type]);
|
||||
},
|
||||
// 获取商家用户概述信息
|
||||
async getShopInfo() {
|
||||
try {
|
||||
const res = await queryAllShopInfo(this.query);
|
||||
this.shopInfo = res;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
sizeChange() {
|
||||
this.tableData.page = 0;
|
||||
this.getTableData();
|
||||
},
|
||||
// 切换状态
|
||||
async statusChange(e, row) {
|
||||
try {
|
||||
this.tableData.loading = true;
|
||||
const data = { ...row };
|
||||
data.status = e;
|
||||
await modityActivate(data);
|
||||
this.getTableData();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
this.tableData.loading = false;
|
||||
}
|
||||
},
|
||||
// 重置查询
|
||||
resetHandle() {
|
||||
this.query.name = "";
|
||||
this.getTableData();
|
||||
},
|
||||
// 分页回调
|
||||
paginationChange(e) {
|
||||
this.tableData.page = e - 1;
|
||||
this.getTableData();
|
||||
},
|
||||
// 获取商品列表
|
||||
async getTableData() {
|
||||
this.tableData.loading = true;
|
||||
try {
|
||||
const res = await queryAllShopUser({
|
||||
...this.query,
|
||||
size: this.tableData.size,
|
||||
page: this.tableData.page + 1,
|
||||
});
|
||||
this.tableData.loading = false;
|
||||
this.tableData.data = res.content;
|
||||
this.tableData.total = res.totalElements;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.user_info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.name {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
::v-deep .el-input--small .el-input__inner{
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
::v-deep .image-slot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #efefef;
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #f5f5f5;
|
||||
padding: 0 14px;
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
padding: 20px 0;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
|
||||
.t {
|
||||
text-align: center;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.n {
|
||||
color: #000;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
padding-top: 6px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.flex{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.gap-20{
|
||||
gap: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
69
src/views/tool/table/components/downloadTableCode.vue
Normal file
69
src/views/tool/table/components/downloadTableCode.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<el-dialog title="下载桌码" width="400px" :visible.sync="dialogVisible" @open="reset">
|
||||
<el-form ref="form" :model="form" label-position="left">
|
||||
<el-form-item label="下载数量">
|
||||
<el-input-number v-model="form.number" :min="1" :max="500"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="onSubmitHandle">下 载</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { downloadFile } from "@/utils/index";
|
||||
import { downloadTableCode } from '@/api/table'
|
||||
export default {
|
||||
props: {
|
||||
total: {
|
||||
type: [Number, String],
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
loading: false,
|
||||
form: {
|
||||
number: 1
|
||||
},
|
||||
resetForm: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.resetForm = { ...this.form }
|
||||
},
|
||||
methods: {
|
||||
async onSubmitHandle() {
|
||||
try {
|
||||
this.loading = true
|
||||
const file = await downloadTableCode({
|
||||
count: this.form.number,
|
||||
shopId: localStorage.getItem('shopId')
|
||||
})
|
||||
this.loading = false
|
||||
this.dialogVisible = false
|
||||
this.$message.success('下载成功')
|
||||
downloadFile(file, "桌码", "zip");
|
||||
} catch (error) {
|
||||
this.loading = false
|
||||
console.log(error);
|
||||
}
|
||||
},
|
||||
show(obj) {
|
||||
this.dialogVisible = true
|
||||
// if (obj && obj.id) {
|
||||
// this.form = JSON.parse(JSON.stringify(obj))
|
||||
// }
|
||||
},
|
||||
close() {
|
||||
this.dialogVisible = false
|
||||
},
|
||||
reset() {
|
||||
this.form = { ...this.resetForm }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
319
src/views/tool/table/components/keyboard.vue
Normal file
319
src/views/tool/table/components/keyboard.vue
Normal file
@@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<div class="simple-Keyboard-number">
|
||||
<div class="carts">
|
||||
<div class="box_status">
|
||||
<slot name="input">
|
||||
<span> {{ number }}</span>
|
||||
</slot>
|
||||
</div>
|
||||
<div class="number_list_box">
|
||||
<div class="yd-keyboard">
|
||||
<div class="mini-number-box1">
|
||||
<div class="mini-number">
|
||||
<div class="key-line">
|
||||
<div class="key" @click="keyboradAdd('1')">1</div>
|
||||
<div class="key" @click="keyboradAdd('2')">2</div>
|
||||
<div class="key" @click="keyboradAdd('3')">3</div>
|
||||
</div>
|
||||
<div class="key-line">
|
||||
<div class="key" @click="keyboradAdd('4')">4</div>
|
||||
<div class="key" @click="keyboradAdd('5')">5</div>
|
||||
<div class="key" @click="keyboradAdd('6')">6</div>
|
||||
</div>
|
||||
<div class="key-line">
|
||||
<div class="key" @click="keyboradAdd('7')">7</div>
|
||||
<div class="key" @click="keyboradAdd('8')">8</div>
|
||||
<div class="key" @click="keyboradAdd('9')">9</div>
|
||||
</div>
|
||||
<div class="key-line">
|
||||
<div class="key" @click="clearFunction">
|
||||
<slot name="clear"> </slot>
|
||||
</div>
|
||||
<div class="key" @click="keyboradAdd('0')">0</div>
|
||||
<div
|
||||
class="key"
|
||||
style="font-size: 31px"
|
||||
@click="keyboradReduce"
|
||||
>
|
||||
<svg
|
||||
t="1723453480343"
|
||||
class="icon"
|
||||
viewBox="0 0 1664 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="1467"
|
||||
width="32"
|
||||
height="32"
|
||||
>
|
||||
<path
|
||||
d="M1526.08 1.6H459.84L41.28 416c-53.76 53.248-53.76 139.52 0 192.64l418.624 414.592v-0.064h1066.176a136.96 136.96 0 0 0 137.6-136.256V137.792a136.96 136.96 0 0 0-137.6-136.192z m-331.392 631.168c26.816 26.624 26.816 69.76 0 96.384-26.88 26.56-70.4 26.56-97.28 0l-121.28-120.128-123.328 122.112a69.76 69.76 0 0 1-97.92 0 68.096 68.096 0 0 1 0-96.96L878.208 512l-121.28-120.064a67.648 67.648 0 0 1 0-96.32c26.88-26.624 70.4-26.624 97.28 0l121.216 120.064 122.24-120.96a69.696 69.696 0 0 1 97.92 0 68.032 68.032 0 0 1 0 96.96l-122.24 120.96 121.344 120.064z"
|
||||
fill="#333333"
|
||||
p-id="1468"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="showConfirm">
|
||||
<div class="submit" @click="keyboradConfirm">确认</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
isCanEmpty: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxTips: {
|
||||
type: String,
|
||||
default: "输入值超范围",
|
||||
},
|
||||
showConfirm: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
default: Infinity,
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
number: 0,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value(newval) {
|
||||
this.number = newval;
|
||||
},
|
||||
number(newval) {
|
||||
this.$emit("input", newval);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clearFunction() {
|
||||
this.$emit("clear", this.number);
|
||||
},
|
||||
keyboradAdd(n) {
|
||||
if (Number(this.number) == 0) {
|
||||
return (this.number = n);
|
||||
}
|
||||
const newval = this.number + n;
|
||||
if (newval > this.max) {
|
||||
return this.$message( this.maxTips);
|
||||
}
|
||||
this.number = newval;
|
||||
},
|
||||
keyboradReduce() {
|
||||
if (this.number.length <= 1) {
|
||||
return (this.number = this.isCanEmpty ? "" : "0");
|
||||
}
|
||||
this.number = `${this.number}`.substring(0, this.number.length - 1);
|
||||
},
|
||||
keyboradConfirm() {
|
||||
this.$emit("confirm", this.number);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.number = `${this.value}`;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.yd-keyboard {
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.mini-number-box1 .mini-number,
|
||||
.yd-keyboard {
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mini-number-box1 .mini-number {
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.mini-number-box1 .mini-number .key-line {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mini-number-box1 .mini-number .key {
|
||||
width: 122px;
|
||||
height: 60px;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
.mini-number-box1 .mini-number .key:hover {
|
||||
background-color: #dcdfe6;
|
||||
}
|
||||
.mini-number-box1 .mini-number .key:not(:last-child) {
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.mini-number-box1 .mini-number .key-line:not(:last-child) {
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.mini-number-box2 {
|
||||
display: flex;
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mini-number-box2 .key {
|
||||
width: 80px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.mini-number-box2 .function-button {
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mini-number-box2 .function-button .key {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.key-line {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.key,
|
||||
.key-line {
|
||||
display: flex;
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.key {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: #fff;
|
||||
-webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.10196078431372549);
|
||||
border-radius: 4px;
|
||||
|
||||
align-items: center;
|
||||
margin-left: 8px;
|
||||
font-family: PingFangSC-Regular;
|
||||
font-weight: 400;
|
||||
font-size: 20px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
text-align: center;
|
||||
line-height: 34px;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.10196078431372549);
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.simple-Keyboard-number,
|
||||
.simple-Keyboard-weight {
|
||||
min-width: 410px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.submit {
|
||||
width: 366px;
|
||||
height: 44px;
|
||||
background: #22bf64;
|
||||
border-radius: 2px;
|
||||
|
||||
display: flex;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
font-family: PingFangSC-Regular;
|
||||
font-weight: 400;
|
||||
margin-bottom: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.disabled-box {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.carts {
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.carts .title,
|
||||
.carts {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.carts .title {
|
||||
justify-content: space-between;
|
||||
height: 64px;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
padding: 20px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.carts .title .left {
|
||||
font-family: PingFangSC-Medium;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.carts .title .right {
|
||||
font-size: 14.4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.carts .box_status {
|
||||
margin-bottom: 20px;
|
||||
width: 370px;
|
||||
height: 58px;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
|
||||
display: flex;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
font-family: MicrosoftYaHei;
|
||||
font-size: 20px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
letter-spacing: 1.25px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.carts .box_status span {
|
||||
padding: 0 5px;
|
||||
}
|
||||
</style>
|
||||
339
src/views/tool/table/components/money-keyboard.vue
Normal file
339
src/views/tool/table/components/money-keyboard.vue
Normal file
@@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<el-dialog width="330px" :title="title" :visible.sync="show">
|
||||
<div class="simple-Keyboard-number">
|
||||
<div class="carts">
|
||||
<div class="pad-14">
|
||||
<div class="box_status">
|
||||
<span class="sym">¥</span>
|
||||
<el-input v-model="number" type="text" @input="numberInput"></el-input>
|
||||
<!-- <span class="inputs" :contenteditable="true" @input="numberInput" type="text" >{{ number }}</span> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="number_list_box">
|
||||
<div class="yd-keyboard">
|
||||
<div class="mini-number-box1">
|
||||
<div class="mini-number">
|
||||
<div class="key-line">
|
||||
<div class="key" @click="keyboradAdd('1')">1</div>
|
||||
<div class="key" @click="keyboradAdd('2')">2</div>
|
||||
<div class="key" @click="keyboradAdd('3')">3</div>
|
||||
<div
|
||||
class="key"
|
||||
style="font-size: 31px"
|
||||
@click="keyboradReduce"
|
||||
>
|
||||
<svg
|
||||
t="1723453480343"
|
||||
class="icon"
|
||||
viewBox="0 0 1664 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="1467"
|
||||
width="32"
|
||||
height="32"
|
||||
>
|
||||
<path
|
||||
d="M1526.08 1.6H459.84L41.28 416c-53.76 53.248-53.76 139.52 0 192.64l418.624 414.592v-0.064h1066.176a136.96 136.96 0 0 0 137.6-136.256V137.792a136.96 136.96 0 0 0-137.6-136.192z m-331.392 631.168c26.816 26.624 26.816 69.76 0 96.384-26.88 26.56-70.4 26.56-97.28 0l-121.28-120.128-123.328 122.112a69.76 69.76 0 0 1-97.92 0 68.096 68.096 0 0 1 0-96.96L878.208 512l-121.28-120.064a67.648 67.648 0 0 1 0-96.32c26.88-26.624 70.4-26.624 97.28 0l121.216 120.064 122.24-120.96a69.696 69.696 0 0 1 97.92 0 68.032 68.032 0 0 1 0 96.96l-122.24 120.96 121.344 120.064z"
|
||||
fill="#333333"
|
||||
p-id="1468"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="key-line">
|
||||
<div class="key" @click="keyboradAdd('4')">4</div>
|
||||
<div class="key" @click="keyboradAdd('5')">5</div>
|
||||
<div class="key" @click="keyboradAdd('6')">6</div>
|
||||
<div class="key" @click="keyboradAdd('clear')">清空</div>
|
||||
</div>
|
||||
<div class="key-line">
|
||||
<div class="key" @click="keyboradAdd('7')">7</div>
|
||||
<div class="key" @click="keyboradAdd('8')">8</div>
|
||||
<div class="key" @click="keyboradAdd('9')">9</div>
|
||||
<div class="key"></div>
|
||||
</div>
|
||||
<div class="key-line">
|
||||
<div class="key" @click="keyboradAdd('.')">.</div>
|
||||
<div class="key" @click="keyboradAdd('0')">0</div>
|
||||
<div class="key" @click="keyboradAdd('00')">00</div>
|
||||
<div class="key"></div>
|
||||
</div>
|
||||
<div class="confirm key" @click="confirm">确认</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="submit" @click="keyboradConfirm">确认</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: "支付",
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
number: '0',
|
||||
show:false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
number(newval) {
|
||||
this.$emit("input", newval);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
confirm(){
|
||||
|
||||
},
|
||||
open(){
|
||||
this.show=true
|
||||
},
|
||||
close(){
|
||||
this.show=false
|
||||
},
|
||||
numberInput(val){
|
||||
console.log(val)
|
||||
this.number=`${Number(val)}`
|
||||
},
|
||||
keyboradAdd(n) {
|
||||
if (n === "clear") {
|
||||
return (this.number = '0');
|
||||
}
|
||||
if(n==='.'){
|
||||
return this.number += this.number.includes('.')?'' : '.'
|
||||
}
|
||||
if (`${this.number}`.length<=1&&Number(this.number)===0) {
|
||||
return (this.number = n);
|
||||
}
|
||||
this.number += n;
|
||||
},
|
||||
keyboradReduce() {
|
||||
if (this.number.length <= 1) {
|
||||
return (this.number = this.isCanEmpty ? "" : "0");
|
||||
}
|
||||
this.number = this.number.substring(0, this.number.length - 1);
|
||||
},
|
||||
keyboradConfirm() {
|
||||
this.$emit("confirm", this.number);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.number = `${this.value}`;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pad-14 {
|
||||
padding: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
::v-deep .el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
::v-deep .el-input__inner::-webkit-outer-spin-button{
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
::v-deep .el-input__inner::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
::v-deep .el-input__inner {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
.number_list_box,
|
||||
.yd-keyboard,
|
||||
.mini-number,
|
||||
.mini-number-box1,
|
||||
.key-line {
|
||||
width: 100%;
|
||||
}
|
||||
.yd-keyboard {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mini-number-box1 .mini-number,
|
||||
.yd-keyboard {
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mini-number-box1 .mini-number {
|
||||
border-top: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.mini-number-box2 {
|
||||
display: flex;
|
||||
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mini-number-box2 .function-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.key,
|
||||
.key-line {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.key {
|
||||
width: 25%;
|
||||
height: 70px;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
align-items: center;
|
||||
font-family: PingFangSC-Regular;
|
||||
font-weight: 400;
|
||||
font-size: 24px;
|
||||
color: #212121;
|
||||
text-align: center;
|
||||
line-height: 34px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.key:hover {
|
||||
background-color: #dcdfe6;
|
||||
}
|
||||
.key:not(:last-child) {
|
||||
border-right: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.key-line:not(:last-child) {
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
}
|
||||
.confirm {
|
||||
height: 142px;
|
||||
width: 25%;
|
||||
background: #22bf64;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-right: none;
|
||||
}
|
||||
.confirm:hover {
|
||||
background: rgba(34, 191, 100, 0.8666666666666667);
|
||||
}
|
||||
.simple-Keyboard-number,
|
||||
.simple-Keyboard-weight {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.submit {
|
||||
width: 366px;
|
||||
height: 44px;
|
||||
background: #22bf64;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
font-family: PingFangSC-Regular;
|
||||
font-weight: 400;
|
||||
margin-bottom: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.disabled-box {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.carts {
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.carts .title,
|
||||
.carts {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.carts .title {
|
||||
justify-content: space-between;
|
||||
height: 64px;
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
padding: 20px;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.carts .title .left {
|
||||
font-family: PingFangSC-Medium;
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.carts .title .right {
|
||||
font-size: 14.4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.box_status {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
padding: 14px;
|
||||
align-items: flex-end;
|
||||
font-family: MicrosoftYaHei;
|
||||
font-size: 20px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
letter-spacing: 1.25px;
|
||||
.sym {
|
||||
font-size: 24px;
|
||||
color: #212121;
|
||||
width: 30px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.inputs {
|
||||
font-size: 28px;
|
||||
color: #212121;
|
||||
outline: none;
|
||||
line-height: 37px;
|
||||
border: none;
|
||||
max-width: 250px;
|
||||
}
|
||||
}
|
||||
::v-deep .el-input--small .el-input__inner{
|
||||
font-size: 28px;
|
||||
color: #212121;
|
||||
outline: none;
|
||||
line-height: 37px;
|
||||
border: none;
|
||||
max-width: 250px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.carts .box_status span {
|
||||
padding: 0 5px;
|
||||
}
|
||||
</style>
|
||||
62
src/views/tool/table/components/order-btn.js
Normal file
62
src/views/tool/table/components/order-btn.js
Normal file
@@ -0,0 +1,62 @@
|
||||
export const orderBtns=[
|
||||
{
|
||||
text: "删除",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "规格",
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
text: "菜品打折",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "赠菜",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "赠菜",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "打包",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "等叫",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "整单等叫",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "单品备注",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "退菜",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "附加费",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "存单",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "取单",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "修改价格",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
text: "撤单",
|
||||
disabled: false,
|
||||
}
|
||||
]
|
||||
76
src/views/tool/table/components/pay-type.vue
Normal file
76
src/views/tool/table/components/pay-type.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="title">选择支付方式</div>
|
||||
<div class="btn_group">
|
||||
<div class="price_select">
|
||||
<div class="pay_btns">
|
||||
<el-button
|
||||
size="medium"
|
||||
@click="changeSel(item)"
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
:type="sel === item.payType ? 'primary' : ''"
|
||||
>
|
||||
{{ item.payName }}
|
||||
</el-button>
|
||||
<!-- <el-button size="medium"> 余额支付 </el-button>
|
||||
<el-button size="medium"> 现金支付 </el-button>
|
||||
<el-button size="medium"> 挂账 </el-button> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { $getPayType } from "@/api/table";
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [],
|
||||
sel: "",
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
sel(newval) {
|
||||
console.log(newval);
|
||||
this.$emit("input", newval);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeSel(item) {
|
||||
this.sel = item.payType;
|
||||
this.$emit("itemClick", item);
|
||||
},
|
||||
async init() {
|
||||
const res = await $getPayType();
|
||||
this.list = res.filter(v=>v.isDisplay);
|
||||
console.log(res[0]);
|
||||
this.sel = this.sel ? this.sel : res[0].payType;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
font-size: 18px;
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
padding: 20px 0;
|
||||
}
|
||||
.pay_btns .el-button {
|
||||
margin-left: 0;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
269
src/views/tool/table/components/subscribe.vue
Normal file
269
src/views/tool/table/components/subscribe.vue
Normal file
@@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="预约"
|
||||
:visible.sync="dialogVisible"
|
||||
width="500px"
|
||||
@close="reset"
|
||||
>
|
||||
<el-form
|
||||
ref="form"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
label-position="left"
|
||||
>
|
||||
|
||||
<el-form-item label="预约日期" prop="bookingDate">
|
||||
<!-- <el-date-picker
|
||||
v-model="form.bookingDate"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
:picker-options="pickerOptions"
|
||||
placeholder="选择预约日期"
|
||||
style="width: 200px;margin-right: 10px;"
|
||||
@change="getShopTableList"
|
||||
/> -->
|
||||
<el-radio-group
|
||||
v-model="form.bookingDate"
|
||||
class="date"
|
||||
@change="getShopTableList"
|
||||
>
|
||||
<el-radio-button
|
||||
v-for="(item) in dateList"
|
||||
:key="item.date"
|
||||
:label="item.date"
|
||||
>
|
||||
<div style="font-size: 12px;margin-bottom: 4px;">{{ item.label }} / {{ item.date.substring(8,10) }}</div>
|
||||
<div style="font-size: 12px;"> {{ item.day }}</div>
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="预约类型" prop="bookingType">
|
||||
<el-radio-group v-model="form.bookingType" @change="getShopTableList">
|
||||
<el-radio-button label="lunch">午餐</el-radio-button>
|
||||
<el-radio-button label="dinner">晚餐</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="就餐时间" prop="bookingTime">
|
||||
<el-time-picker
|
||||
v-model="form.bookingTime"
|
||||
value-format="HH:mm:ss"
|
||||
placeholder="选择时间"
|
||||
style="width: 100%;"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="预约桌台" prop="shopTableId">
|
||||
<el-select v-model="form.shopTableId" placeholder="请选择" style="width: 100%;">
|
||||
<el-option
|
||||
v-for="item in shopTableList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="用餐人数" prop="dinerNum">
|
||||
<el-input-number v-model="form.dinerNum" :min="1" controls-position="right" style="width: 100%;"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="订餐人" prop="bookingPerson">
|
||||
<el-input v-model="form.bookingPerson" style="width: 200px;margin-right: 10px;" placeholder="请输入联系人姓名" />
|
||||
<el-radio-group v-model="form.gender">
|
||||
<el-radio-button label="1">先生</el-radio-button>
|
||||
<el-radio-button label="2">女士</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话" prop="phoneNumber">
|
||||
<el-input v-model="form.phoneNumber" oninput="value= value.replace(/[^0-9]/g, '')" maxlength="11" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="onSubmitHandle"
|
||||
>确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getShopTableList, makeShopTable } from '@/api/table'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
resetForm: '',
|
||||
loading: false,
|
||||
pickerOptions: {
|
||||
disabledDate(time) {
|
||||
return time.getTime() < Date.now() - 8.64e7 // 如果今天之前的日期都不可选,减去8.64e7是1天的毫秒数
|
||||
}
|
||||
},
|
||||
dateList: [
|
||||
{ label: '今', day: '', date: '' },
|
||||
{ label: '明', day: '', date: '' },
|
||||
{ label: '后', day: '', date: '' }
|
||||
],
|
||||
shopTableList: [],
|
||||
form: {
|
||||
bookingDate: '',
|
||||
bookingType: 'lunch',
|
||||
shopTableId: null,
|
||||
dinerNum: 1,
|
||||
bookingPerson: '',
|
||||
phoneNumber: '',
|
||||
gender: '1',
|
||||
bookingTime: '',
|
||||
diningType: '普通用餐',
|
||||
focus: '0',
|
||||
receiveMarketingSms: '0'
|
||||
|
||||
},
|
||||
rules: {
|
||||
shopTableId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择预约桌台',
|
||||
trigger: ['blur']
|
||||
}
|
||||
],
|
||||
dinerNum: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择用餐人数',
|
||||
trigger: ['blur']
|
||||
}
|
||||
],
|
||||
phoneNumber: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入联系方式',
|
||||
trigger: ['blur']
|
||||
}
|
||||
],
|
||||
bookingPerson: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入订餐人姓名',
|
||||
trigger: ['blur']
|
||||
}
|
||||
],
|
||||
bookingTime: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择用餐时间',
|
||||
trigger: ['change']
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.resetForm = { ...this.form }
|
||||
},
|
||||
methods: {
|
||||
getWeekdays() {
|
||||
const today = new Date()
|
||||
const tomorrow = new Date(today)
|
||||
const dayAfterTomorrow = new Date(today)
|
||||
|
||||
// 设置时间为今天
|
||||
tomorrow.setDate(today.getDate() + 1)
|
||||
dayAfterTomorrow.setDate(today.getDate() + 2)
|
||||
|
||||
// 获取星期几的方法
|
||||
const getDayOfWeek = (date) => {
|
||||
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||||
return days[date.getDay()]
|
||||
}
|
||||
|
||||
// 获取今天、明天和后天的星期几
|
||||
const todayWeekday = getDayOfWeek(today)
|
||||
const tomorrowWeekday = getDayOfWeek(tomorrow)
|
||||
const dayAfterTomorrowWeekday = getDayOfWeek(dayAfterTomorrow)
|
||||
this.dateList[0].day = todayWeekday
|
||||
this.dateList[1].day = tomorrowWeekday
|
||||
this.dateList[2].day = dayAfterTomorrowWeekday
|
||||
this.dateList[0].date = dayjs(today).format('YYYY-MM-DD')
|
||||
this.dateList[1].date = dayjs(tomorrow).format('YYYY-MM-DD')
|
||||
this.dateList[2].date = dayjs(dayAfterTomorrow).format('YYYY-MM-DD')
|
||||
|
||||
this.form.bookingDate = this.dateList[0].date
|
||||
},
|
||||
/**
|
||||
* 获取桌台数据
|
||||
*/
|
||||
async getShopTableList() {
|
||||
this.form.shopTableId = null
|
||||
const params = {
|
||||
bookingDate: this.form.bookingDate,
|
||||
bookingType: this.form.bookingType
|
||||
}
|
||||
const res = await getShopTableList(params)
|
||||
this.shopTableList = res
|
||||
console.log(res)
|
||||
},
|
||||
onSubmitHandle() {
|
||||
this.$refs.form.validate(async(valid) => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
try {
|
||||
const bookingTime = this.form.bookingDate + ' ' + this.form.bookingTime
|
||||
const params = {
|
||||
...this.form,
|
||||
shopId: localStorage.getItem('shopId')
|
||||
}
|
||||
params.bookingTime = bookingTime
|
||||
const res = await makeShopTable(params)
|
||||
this.$emit('success', res)
|
||||
this.close()
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: `预约成功`,
|
||||
type: 'success'
|
||||
})
|
||||
this.loading = false
|
||||
} catch (error) {
|
||||
this.loading = false
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
show(obj) {
|
||||
this.dialogVisible = true
|
||||
if (obj && obj.id) {
|
||||
this.form = JSON.parse(JSON.stringify(obj))
|
||||
}
|
||||
this.getShopTableList()
|
||||
this.getWeekdays()
|
||||
},
|
||||
close() {
|
||||
this.dialogVisible = false
|
||||
},
|
||||
reset() {
|
||||
this.form = { ...this.resetForm }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.date{
|
||||
::v-deep .el-form-item__content{
|
||||
// height: 36px;
|
||||
// display: flex;
|
||||
// justify-content: space-between;
|
||||
}
|
||||
::v-deep .el-radio-button{
|
||||
height: 100%;
|
||||
}
|
||||
::v-deep .el-radio-button__inner{
|
||||
padding: 4px 20px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
3990
src/views/tool/table/components/table-diancan copy.vue
Normal file
3990
src/views/tool/table/components/table-diancan copy.vue
Normal file
File diff suppressed because one or more lines are too long
3804
src/views/tool/table/components/table-diancan-back.vue
Normal file
3804
src/views/tool/table/components/table-diancan-back.vue
Normal file
File diff suppressed because one or more lines are too long
3839
src/views/tool/table/components/table-diancan-oldList-back.vue
Normal file
3839
src/views/tool/table/components/table-diancan-oldList-back.vue
Normal file
File diff suppressed because one or more lines are too long
4302
src/views/tool/table/components/table-diancan.vue
Normal file
4302
src/views/tool/table/components/table-diancan.vue
Normal file
File diff suppressed because one or more lines are too long
141
src/views/tool/table/components/table-edit.vue
Normal file
141
src/views/tool/table/components/table-edit.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑台桌' : '添加台桌'" :visible.sync="dialogVisible" @open="tbShopAreaGet" @close="reset">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px" label-position="left">
|
||||
<el-form-item label="选择区域" prop="areaId">
|
||||
<el-select v-model="form.areaId" placeholder="请选择区域">
|
||||
<el-option :label="item.name" :value="item.id" v-for="item in areaList" :key="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="台桌状态" prop="status" v-if="form.id">
|
||||
<el-select v-model="form.status" placeholder="请选择台桌状态">
|
||||
<el-option :label="item.label" :value="item.value" v-for="item in status" :key="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="台桌名称">
|
||||
<el-input v-model="form.name" placeholder="请输入台桌名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="客座数">
|
||||
<el-input-number v-model="form.maxCapacity" :min="0" controls-position="right"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="清台管理">
|
||||
<el-radio-group v-model="form.autoClear">
|
||||
<el-radio-button :label="0">手动清台</el-radio-button>
|
||||
<el-radio-button :label="1">自动清台</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="网络预定开关">
|
||||
<el-switch v-model="form.isPredate" :active-value="1" :inactive-value="2"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio-button :label="0">低消</el-radio-button>
|
||||
<el-radio-button :label="2">计时</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="最低消费" v-if="form.type == 0">
|
||||
<el-input-number v-model="form.amount" :min="0" controls-position="right"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="每小时收费" v-if="form.type == 2">
|
||||
<el-input-number v-model="form.perhour" :min="0" controls-position="right"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="onSubmitHandle">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { tbShopTable, tbShopAreaGet } from '@/api/table'
|
||||
import $status from "../status.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
resetForm: '',
|
||||
loading: false,
|
||||
status:[],
|
||||
form: {
|
||||
id: '',
|
||||
name: '',
|
||||
areaId: '',
|
||||
maxCapacity: 0,
|
||||
isPredate: 1,
|
||||
type: 2,
|
||||
perhour: 0,
|
||||
amount: 0,
|
||||
autoClear: 1,
|
||||
|
||||
},
|
||||
rules: {
|
||||
areaId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择区域',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
},
|
||||
areaList: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.resetForm = { ...this.form }
|
||||
this.status = Object.keys($status).map(key =>{
|
||||
return {...$status[key],value:key}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
onSubmitHandle() {
|
||||
this.$refs.form.validate(async valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
try {
|
||||
let res = await tbShopTable({
|
||||
...this.form,
|
||||
qrcode:this.form.tableId,
|
||||
shopId: localStorage.getItem('shopId')
|
||||
}, this.form.id ? 'put' : 'post')
|
||||
this.$emit('success', res)
|
||||
this.close()
|
||||
this.$notify({
|
||||
title: '成功',
|
||||
message: `${this.form.id ? '编辑' : '添加'}成功`,
|
||||
type: 'success'
|
||||
});
|
||||
this.loading = false
|
||||
} catch (error) {
|
||||
this.loading = false
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
show(obj) {
|
||||
this.dialogVisible = true
|
||||
if (obj && obj.id) {
|
||||
this.form = JSON.parse(JSON.stringify(obj))
|
||||
}
|
||||
},
|
||||
close() {
|
||||
this.dialogVisible = false
|
||||
},
|
||||
reset() {
|
||||
this.form = { ...this.resetForm }
|
||||
},
|
||||
// 获取区域
|
||||
async tbShopAreaGet() {
|
||||
try {
|
||||
const { content } = await tbShopAreaGet({
|
||||
shopId: localStorage.getItem('shopId')
|
||||
})
|
||||
this.areaList = content
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
47
src/views/tool/table/components/util.js
Normal file
47
src/views/tool/table/components/util.js
Normal file
@@ -0,0 +1,47 @@
|
||||
//判断商品是否可以下单
|
||||
export function isCanBuy(goods,isStock) {
|
||||
return goods.isGrounding && goods.isPauseSale == 0 && (isStock?goods.stockNumber > 0:true) ;
|
||||
}
|
||||
|
||||
// 一个数组是否包含另外一个数组全部元素
|
||||
export function arrayContainsAll(arr1, arr2) {
|
||||
for (let i = 0; i < arr2.length; i++) {
|
||||
if (!arr1.includes(arr2[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//n项 n-1项组合,生成全部结果
|
||||
export function generateCombinations(arr, k) {
|
||||
let result = [];
|
||||
|
||||
function helper(index, current) {
|
||||
if (current.length === k) {
|
||||
result.push(current.slice()); // 使用slice()来避免直接修改原始数组
|
||||
} else {
|
||||
for (let i = index; i < arr.length; i++) {
|
||||
current.push(arr[i]); // 将当前元素添加到组合中
|
||||
helper(i + 1, current); // 递归调用,索引增加以避免重复选择相同的元素
|
||||
current.pop(); // 回溯,移除当前元素以便尝试其他组合
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
helper(0, []); // 从索引0开始,初始空数组作为起点
|
||||
return result;
|
||||
}
|
||||
|
||||
export function returnReverseVal(val, isReturnString = true) {
|
||||
const isBol = typeof val === "boolean";
|
||||
const isString = typeof val === "string";
|
||||
let reverseNewval = "";
|
||||
if (isBol) {
|
||||
reverseNewval = !val;
|
||||
}
|
||||
if (isString) {
|
||||
reverseNewval = val === "true" ? "false" : "true";
|
||||
}
|
||||
return reverseNewval;
|
||||
}
|
||||
1
src/views/tool/table/index.vue
Normal file
1
src/views/tool/table/index.vue
Normal file
@@ -0,0 +1 @@
|
||||
<template></template>
|
||||
10
src/views/tool/table/status.js
Normal file
10
src/views/tool/table/status.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export default {
|
||||
pending: { label: '挂单中', type: '#E6A23C' },
|
||||
cleaning: { label: '待清台', type: '#FAAD14' },
|
||||
using: { label: '开台中', type: '#FF4D4F' },
|
||||
idle: { label: '空闲', type: '#3F9EFF' },
|
||||
paying: { label: '结算中', type: '#E6A23C' },
|
||||
closed: { label: '关台', type: '#DDDDDD' },
|
||||
subscribe: { label: '预约', type: '#52C41A ' },
|
||||
unbind: { label: '未绑定', type: 'rgb(221,221,221)' }
|
||||
}
|
||||
155
src/views/user/active/index.vue
Normal file
155
src/views/user/active/index.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 列表 -->
|
||||
<!-- 搜索 -->
|
||||
<page-search
|
||||
ref="searchRef"
|
||||
:search-config="searchConfig"
|
||||
@query-click="handleQueryClick"
|
||||
@reset-click="handleResetClick"
|
||||
/>
|
||||
<!-- 列表 -->
|
||||
<page-content
|
||||
ref="contentRef"
|
||||
:content-config="contentConfig"
|
||||
@add-click="handleAddClick"
|
||||
@edit-click="handleEditClick"
|
||||
@export-click="handleExportClick"
|
||||
@search-click="handleSearchClick"
|
||||
@toolbar-click="handleToolbarClick"
|
||||
@operat-click="handleOperatClick"
|
||||
@filter-change="handleFilterChange"
|
||||
>
|
||||
<template #status="scope">
|
||||
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
|
||||
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #options="scope">
|
||||
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
|
||||
</template>
|
||||
<template #bol="scope">
|
||||
{{ scope.row[scope.prop] ? "是" : "否" }}
|
||||
</template>
|
||||
<template #gender="scope">
|
||||
<el-tag
|
||||
:type="
|
||||
scope.row[scope.prop] == null
|
||||
? 'info'
|
||||
: scope.row[scope.prop] == 1
|
||||
? 'success'
|
||||
: 'warning'
|
||||
"
|
||||
>
|
||||
{{ scope.row[scope.prop] === null ? "未知" : scope.row[scope.prop] == 1 ? "男" : "女" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #user="scope">
|
||||
<div class="flex align-center">
|
||||
<el-avatar :src="scope.row.headImg" />
|
||||
<el-tag>{{ scope.row.nickName }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<template #link="scope">
|
||||
<el-link>{{ scope.row[scope.prop] }}</el-link>
|
||||
</template>
|
||||
<template #mobile="scope">
|
||||
<el-text>{{ scope.row[scope.prop] }}</el-text>
|
||||
<copy-button
|
||||
v-if="scope.row[scope.prop]"
|
||||
:text="scope.row[scope.prop]"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</template>
|
||||
</page-content>
|
||||
|
||||
<!-- 新增 -->
|
||||
<page-modal
|
||||
ref="addModalRef"
|
||||
:modal-config="addModalConfig"
|
||||
@submit-click="handleSubmitClick"
|
||||
@formDataChange="formDataChange"
|
||||
></page-modal>
|
||||
|
||||
<!-- 编辑 -->
|
||||
<page-modal
|
||||
ref="editModalRef"
|
||||
:modal-config="editModalConfig"
|
||||
@submit-click="handleSubmitClick"
|
||||
@formDataChange="formDataChange"
|
||||
></page-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup >
|
||||
import usePage from "@/components/CURD/usePage";
|
||||
import addModalConfig from "./config/add";
|
||||
import contentConfig from "./config/content";
|
||||
import editModalConfig from "./config/edit";
|
||||
import searchConfig from "./config/search";
|
||||
import { returnOptionsLabel } from "./config/config";
|
||||
const editMoneyModalRef = ref(null);
|
||||
const {
|
||||
searchRef,
|
||||
contentRef,
|
||||
addModalRef,
|
||||
editModalRef,
|
||||
handleQueryClick,
|
||||
handleResetClick,
|
||||
// handleAddClick,
|
||||
// handleEditClick,
|
||||
handleSubmitClick,
|
||||
handleExportClick,
|
||||
handleSearchClick,
|
||||
handleFilterChange,
|
||||
} = usePage();
|
||||
|
||||
// 修改金额表单类型
|
||||
function formDataChange(type, val) {
|
||||
console.log(type, val);
|
||||
}
|
||||
|
||||
// 新增
|
||||
async function handleAddClick() {
|
||||
addModalRef.value?.setModalVisible();
|
||||
}
|
||||
// 获取优惠券
|
||||
async function getCouponList() {}
|
||||
// 编辑
|
||||
async function handleEditClick(row) {
|
||||
editModalRef.value?.handleDisabled(false);
|
||||
editModalRef.value?.setModalVisible();
|
||||
// 根据id获取数据进行填充
|
||||
// const data = await VersionApi.getFormData(row.id);
|
||||
editModalRef.value?.setFormData({ ...row, headImg: row.headImg ? [row.headImg] : "" });
|
||||
}
|
||||
|
||||
// 其他工具栏
|
||||
function handleToolbarClick(name) {
|
||||
console.log(name);
|
||||
if (name === "custom1") {
|
||||
ElMessage.success("点击了自定义1按钮");
|
||||
}
|
||||
}
|
||||
|
||||
// 其他操作列
|
||||
async function handleOperatClick(data) {
|
||||
const row = data.row;
|
||||
if (data.name == "more") {
|
||||
if (data.command === "change-money") {
|
||||
editMoneyModalRef.value.setModalVisible();
|
||||
editMoneyModalRef.value.setFormData({ ...row, headImg: row.headImg ? [row.headImg] : "" });
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 切换示例
|
||||
const isA = ref(true);
|
||||
</script>
|
||||
<style scoped>
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
135
src/views/user/list/config/add.ts
Normal file
135
src/views/user/list/config/add.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import shopUserApi, { type addRequest } from "@/api/account/shopUser";
|
||||
import type { IModalConfig } from "@/components/CURD/types";
|
||||
|
||||
const modalConfig: IModalConfig<addRequest> = {
|
||||
pageName: "sys:user",
|
||||
dialog: {
|
||||
title: "添加用户",
|
||||
width: 800,
|
||||
draggable: true,
|
||||
},
|
||||
form: {
|
||||
labelWidth: 140,
|
||||
},
|
||||
formAction: function (data: addRequest) {
|
||||
return shopUserApi.add(data.shopid, data);
|
||||
},
|
||||
beforeSubmit(data) {
|
||||
console.log("提交之前处理", data);
|
||||
},
|
||||
formItems: [
|
||||
{
|
||||
label: "用户头像",
|
||||
prop: "headImg",
|
||||
rules: [{ required: false, message: "请选择用户头像", trigger: "blur" }],
|
||||
type: "UpImage",
|
||||
attrs: {
|
||||
placeholder: "请选择用户头像",
|
||||
},
|
||||
|
||||
},
|
||||
{
|
||||
label: "用户昵称",
|
||||
prop: "nickName",
|
||||
rules: [{ required: true, message: "请输入用户昵称", trigger: "blur" }],
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "请输入用户昵称",
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "input",
|
||||
label: "手机号码",
|
||||
prop: "phone",
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
message: "请输入正确的手机号码",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
attrs: {
|
||||
placeholder: "请输入手机号码",
|
||||
maxlength: 11,
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "会员生日",
|
||||
prop: "birthDay",
|
||||
rules: [{ required: false, message: "请选择会员生日", trigger: "blur" }],
|
||||
type: "date-picker",
|
||||
attrs: {
|
||||
placeholder: "请选择会员生日",
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "radio",
|
||||
label: "性别",
|
||||
prop: "sex",
|
||||
rules: [{ required: false, message: "请选择性别", trigger: "blur" }],
|
||||
attrs: {
|
||||
placeholder: "请选择性别",
|
||||
},
|
||||
initialValue: '',
|
||||
options: [
|
||||
{ label: '男', value: 1 },
|
||||
{ label: '女', value: 0 },
|
||||
],
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "账户积分",
|
||||
prop: "accountPoints",
|
||||
rules: [{ required: false, message: "请输入账户积分", trigger: "blur" }],
|
||||
type: "input-number",
|
||||
attrs: {
|
||||
placeholder: "请输入账户积分",
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "钱包余额",
|
||||
prop: "amount",
|
||||
rules: [{ required: false, message: "请输入钱包余额", trigger: "blur" }],
|
||||
type: "input-number",
|
||||
attrs: {
|
||||
placeholder: "请输入钱包余额",
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "radio",
|
||||
label: "是否会员",
|
||||
prop: "isVip",
|
||||
options: [
|
||||
{ label: '是', value: 1 },
|
||||
{ label: '否', value: 0 },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 如果有异步数据会修改配置的,推荐用reactive包裹,而纯静态配置的可以直接导出
|
||||
export default reactive(modalConfig);
|
||||
46
src/views/user/list/config/config.ts
Normal file
46
src/views/user/list/config/config.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
export const isVipOptions: statusOptions[] = [
|
||||
{ label: "全部", value: "" },
|
||||
{ label: "非会员", value: "0" },
|
||||
{ label: "会员", value: "1" },
|
||||
|
||||
];
|
||||
|
||||
export type optionsType = "isVip";
|
||||
|
||||
export function returnOptions(type: optionsType) {
|
||||
if (type === "isVip") {
|
||||
return isVipOptions;
|
||||
}
|
||||
}
|
||||
|
||||
export function returnOptionsLabel(optionsType: optionsType, value: string | number) {
|
||||
const options = returnOptions(optionsType);
|
||||
if (!options) {
|
||||
return "";
|
||||
}
|
||||
const option = options.find((item) => item.value === value);
|
||||
return option ? option.label : "";
|
||||
}
|
||||
|
||||
export interface options {
|
||||
label: string;
|
||||
value: string | number;
|
||||
[property: string]: any;
|
||||
}
|
||||
export interface statusOptions extends options {
|
||||
}
|
||||
|
||||
export type payTypeValue =
|
||||
| ""
|
||||
| "cash"
|
||||
| "bank"
|
||||
| "scanCode"
|
||||
| "deposit"
|
||||
| "vipPay"
|
||||
| "arrears"
|
||||
| "virtual"
|
||||
| "arrears";
|
||||
|
||||
export interface payTypeOptions extends options {
|
||||
value: payTypeValue;
|
||||
}
|
||||
106
src/views/user/list/config/content.ts
Normal file
106
src/views/user/list/config/content.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import shopUserApi, { type getListRequest } from "@/api/account/shopUser";
|
||||
import type { IContentConfig } from "@/components/CURD/types";
|
||||
|
||||
const contentConfig: IContentConfig<any> = {
|
||||
pageName: "sys:user",
|
||||
table: {
|
||||
border: true,
|
||||
highlightCurrentRow: true,
|
||||
},
|
||||
pagination: {
|
||||
background: true,
|
||||
layout: "prev,pager,next,jumper,total,sizes",
|
||||
pageSize: 20,
|
||||
pageSizes: [10, 20, 30, 50],
|
||||
},
|
||||
indexAction: function (params: getListRequest) {
|
||||
return shopUserApi.getList(params);
|
||||
},
|
||||
// deleteAction: shopUserApi.delete,
|
||||
// modifyAction: function (data) {
|
||||
// // return shopUserApi.edit(data);
|
||||
// },
|
||||
pk: "id",
|
||||
toolbar: ["add"],
|
||||
defaultToolbar: ["refresh", "filter", "search"],
|
||||
cols: [
|
||||
{ type: "selection", width: 50, align: "center" },
|
||||
{ label: "id", align: "center", prop: "id", width: 100, show: true },
|
||||
{
|
||||
label: "用户",
|
||||
align: "center",
|
||||
prop: "user",
|
||||
templet: "custom",
|
||||
slotName: "user",
|
||||
},
|
||||
{
|
||||
label: "性别",
|
||||
align: "center",
|
||||
prop: "sex",
|
||||
templet: "custom",
|
||||
slotName: "gender",
|
||||
},
|
||||
{
|
||||
label: "会员",
|
||||
align: "center",
|
||||
prop: "isVip",
|
||||
templet: "custom",
|
||||
slotName: "bol",
|
||||
},
|
||||
{
|
||||
label: "手机号",
|
||||
align: "center",
|
||||
prop: "phone",
|
||||
width: 140,
|
||||
templet: "custom",
|
||||
slotName: "mobile",
|
||||
},
|
||||
{
|
||||
label: "余额",
|
||||
align: "center",
|
||||
prop: "amount",
|
||||
},
|
||||
{
|
||||
label: "积分",
|
||||
align: "center",
|
||||
prop: "accountPoints",
|
||||
},
|
||||
{
|
||||
label: "消费累计",
|
||||
align: "center",
|
||||
prop: "consumeAmount",
|
||||
},
|
||||
{
|
||||
label: "消费次数累计",
|
||||
align: "center",
|
||||
prop: "consumeAmount",
|
||||
},
|
||||
{
|
||||
label: "注册时间",
|
||||
align: "center",
|
||||
prop: "createTime",
|
||||
},
|
||||
{
|
||||
label: "操作",
|
||||
align: "center",
|
||||
fixed: "right",
|
||||
width: 180,
|
||||
templet: "tool",
|
||||
operat: [
|
||||
{
|
||||
name: "edit",
|
||||
text: "编辑",
|
||||
},
|
||||
{
|
||||
name: "more",
|
||||
text: "更多",
|
||||
options: [
|
||||
{ label: '增减余额', command: 'change-money' }
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default contentConfig;
|
||||
145
src/views/user/list/config/edit-money.ts
Normal file
145
src/views/user/list/config/edit-money.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import shopUserApi, { type editMoneyRequest } from "@/api/account/shopUser";
|
||||
import type { IModalConfig } from "@/components/CURD/types";
|
||||
import { min } from "lodash";
|
||||
|
||||
// 增减或者减少余额的类型
|
||||
export interface optuions {
|
||||
label: string;
|
||||
value: string | number;
|
||||
}
|
||||
export const addOptions: optuions[] = [
|
||||
{ label: '增加充值', value: 'adminIn' },
|
||||
{ label: '增加消费退款', value: 'adminRefund' },
|
||||
]
|
||||
export const reduceOptions: optuions[] = [
|
||||
{ label: '扣除消费', value: 'adminOut' },
|
||||
{ label: '扣除充值退款', value: 'adminInOut' },
|
||||
]
|
||||
|
||||
|
||||
const modalConfig: IModalConfig<editMoneyRequest> = {
|
||||
pageName: "sys:user",
|
||||
dialog: {
|
||||
title: "增减余额",
|
||||
width: 800,
|
||||
draggable: true,
|
||||
},
|
||||
pk: "id",
|
||||
form: {
|
||||
labelWidth: 140,
|
||||
},
|
||||
formAction: function (data) {
|
||||
return shopUserApi.editMoney(data.shopid, data);
|
||||
},
|
||||
beforeSubmit(data) {
|
||||
console.log("提交之前处理", data);
|
||||
},
|
||||
formItems: [
|
||||
{
|
||||
label: "用户昵称",
|
||||
prop: "nickName",
|
||||
rules: [{ required: false, message: "请输入用户昵称", trigger: "blur" }],
|
||||
type: "input",
|
||||
disabled: true,
|
||||
attrs: {
|
||||
placeholder: "请输入用户昵称",
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "钱包余额",
|
||||
prop: "amount",
|
||||
disabled: true,
|
||||
rules: [{ required: false, message: "", trigger: "blur" }],
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "",
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "radio",
|
||||
label: "增减余额",
|
||||
prop: "type",
|
||||
rules: [{ required: true, message: "增减余额", trigger: "blur" }],
|
||||
initialValue: 1,
|
||||
options: [
|
||||
{ label: '增加', value: 1 },
|
||||
{ label: '减少', value: 0 },
|
||||
],
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
watch() {
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "金额",
|
||||
prop: "money",
|
||||
rules: [{ required: false, message: "请输入要增加或者减少的金额", trigger: "blur" }],
|
||||
type: "input-number",
|
||||
attrs: {
|
||||
placeholder: "请输入要增加或者减少的金额",
|
||||
min: 0,
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
initialValue: 0
|
||||
},
|
||||
{
|
||||
label: "类型",
|
||||
prop: "bizEnum",
|
||||
rules: [{ required: true, message: "请选择类型", trigger: "blur" }],
|
||||
type: "radio-button",
|
||||
attrs: {
|
||||
placeholder: "请选择类型",
|
||||
},
|
||||
options: addOptions,
|
||||
},
|
||||
{
|
||||
label: "备注",
|
||||
prop: "remark",
|
||||
rules: [{ required: false, message: "请输入备注", trigger: "blur" }],
|
||||
type: "textarea",
|
||||
attrs: {
|
||||
placeholder: "请输入备注",
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: "钱包余额",
|
||||
// prop: "amount",
|
||||
// rules: [{ required: false, message: "请输入钱包余额", trigger: "blur" }],
|
||||
// type: "input-number",
|
||||
// attrs: {
|
||||
// placeholder: "请输入钱包余额",
|
||||
// },
|
||||
// col: {
|
||||
// xs: 24,
|
||||
// sm: 12,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// type: "radio",
|
||||
// label: "是否会员",
|
||||
// prop: "isVip",
|
||||
// options: [
|
||||
// { label: '是', value: 1 },
|
||||
// { label: '否', value: 0 },
|
||||
// ],
|
||||
// },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
// 如果有异步数据会修改配置的,推荐用reactive包裹,而纯静态配置的可以直接导出
|
||||
export default reactive(modalConfig);
|
||||
136
src/views/user/list/config/edit.ts
Normal file
136
src/views/user/list/config/edit.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import shopUserApi, { type editRequest } from "@/api/account/shopUser";
|
||||
import type { IModalConfig } from "@/components/CURD/types";
|
||||
|
||||
const modalConfig: IModalConfig<editRequest> = {
|
||||
pageName: "sys:user",
|
||||
dialog: {
|
||||
title: "修改用户",
|
||||
width: 800,
|
||||
draggable: true,
|
||||
},
|
||||
pk: "id",
|
||||
form: {
|
||||
labelWidth: 140,
|
||||
},
|
||||
formAction: function (data: editRequest) {
|
||||
return shopUserApi.edit(data.shopid, data);
|
||||
},
|
||||
beforeSubmit(data) {
|
||||
console.log("提交之前处理", data);
|
||||
},
|
||||
formItems: [
|
||||
// {
|
||||
// label: "用户头像",
|
||||
// prop: "headImg",
|
||||
// rules: [{ required: false, message: "请选择用户头像", trigger: "blur" }],
|
||||
// type: "UpImage",
|
||||
// attrs: {
|
||||
// placeholder: "请选择用户头像",
|
||||
// },
|
||||
|
||||
// },
|
||||
{
|
||||
label: "用户昵称",
|
||||
prop: "nickName",
|
||||
rules: [{ required: false, message: "请输入用户昵称", trigger: "blur" }],
|
||||
type: "input",
|
||||
attrs: {
|
||||
placeholder: "请输入用户昵称",
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// type: "input",
|
||||
// label: "手机号码",
|
||||
// prop: "phone",
|
||||
// rules: [
|
||||
// {
|
||||
// required: true,
|
||||
// pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
// message: "请输入正确的手机号码",
|
||||
// trigger: "blur",
|
||||
// },
|
||||
// ],
|
||||
// attrs: {
|
||||
// placeholder: "请输入手机号码",
|
||||
// maxlength: 11,
|
||||
// },
|
||||
// col: {
|
||||
// xs: 24,
|
||||
// sm: 12,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
label: "会员生日",
|
||||
prop: "birthDay",
|
||||
rules: [{ required: false, message: "请选择会员生日", trigger: "blur" }],
|
||||
type: "date-picker",
|
||||
attrs: {
|
||||
placeholder: "请选择会员生日",
|
||||
},
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "radio",
|
||||
label: "性别",
|
||||
prop: "sex",
|
||||
rules: [{ required: false, message: "请选择性别", trigger: "blur" }],
|
||||
attrs: {
|
||||
placeholder: "请选择性别",
|
||||
},
|
||||
initialValue: '',
|
||||
options: [
|
||||
{ label: '男', value: 1 },
|
||||
{ label: '女', value: 0 },
|
||||
],
|
||||
col: {
|
||||
xs: 24,
|
||||
sm: 12,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// label: "账户积分",
|
||||
// prop: "accountPoints",
|
||||
// rules: [{ required: false, message: "请输入账户积分", trigger: "blur" }],
|
||||
// type: "input-number",
|
||||
// attrs: {
|
||||
// placeholder: "请输入账户积分",
|
||||
// },
|
||||
// col: {
|
||||
// xs: 24,
|
||||
// sm: 12,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// label: "钱包余额",
|
||||
// prop: "amount",
|
||||
// rules: [{ required: false, message: "请输入钱包余额", trigger: "blur" }],
|
||||
// type: "input-number",
|
||||
// attrs: {
|
||||
// placeholder: "请输入钱包余额",
|
||||
// },
|
||||
// col: {
|
||||
// xs: 24,
|
||||
// sm: 12,
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// type: "radio",
|
||||
// label: "是否会员",
|
||||
// prop: "isVip",
|
||||
// options: [
|
||||
// { label: '是', value: 1 },
|
||||
// { label: '否', value: 0 },
|
||||
// ],
|
||||
// },
|
||||
],
|
||||
};
|
||||
|
||||
// 如果有异步数据会修改配置的,推荐用reactive包裹,而纯静态配置的可以直接导出
|
||||
export default reactive(modalConfig);
|
||||
35
src/views/user/list/config/search.ts
Normal file
35
src/views/user/list/config/search.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import type { ISearchConfig } from "@/components/CURD/types";
|
||||
import { isVipOptions } from "./config";
|
||||
const searchConfig: ISearchConfig = {
|
||||
pageName: "sys:user",
|
||||
inline: true,
|
||||
isExpandable: false,
|
||||
formItems: [
|
||||
{
|
||||
type: "radio-button",
|
||||
label: "是否会员",
|
||||
prop: "isVip",
|
||||
attrs: {
|
||||
placeholder: "请选择",
|
||||
clearable: true,
|
||||
},
|
||||
options: isVipOptions,
|
||||
},
|
||||
|
||||
{
|
||||
type: "input",
|
||||
label: "昵称或手机号",
|
||||
prop: "key",
|
||||
attrs: {
|
||||
placeholder: "请输入昵称或手机号",
|
||||
clearable: true,
|
||||
style: {
|
||||
width: "200px",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
],
|
||||
};
|
||||
|
||||
export default searchConfig;
|
||||
169
src/views/user/list/index.vue
Normal file
169
src/views/user/list/index.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 列表 -->
|
||||
<!-- 搜索 -->
|
||||
<page-search
|
||||
ref="searchRef"
|
||||
:search-config="searchConfig"
|
||||
@query-click="handleQueryClick"
|
||||
@reset-click="handleResetClick"
|
||||
/>
|
||||
<!-- 列表 -->
|
||||
<page-content
|
||||
ref="contentRef"
|
||||
:content-config="contentConfig"
|
||||
@add-click="handleAddClick"
|
||||
@edit-click="handleEditClick"
|
||||
@export-click="handleExportClick"
|
||||
@search-click="handleSearchClick"
|
||||
@toolbar-click="handleToolbarClick"
|
||||
@operat-click="handleOperatClick"
|
||||
@filter-change="handleFilterChange"
|
||||
>
|
||||
<template #status="scope">
|
||||
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
|
||||
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #options="scope">
|
||||
{{ returnOptionsLabel(scope.prop, scope.row[scope.prop]) }}
|
||||
</template>
|
||||
<template #bol="scope">
|
||||
{{ scope.row[scope.prop] ? "是" : "否" }}
|
||||
</template>
|
||||
<template #gender="scope">
|
||||
<el-tag
|
||||
:type="
|
||||
scope.row[scope.prop] == null
|
||||
? 'info'
|
||||
: scope.row[scope.prop] == 1
|
||||
? 'success'
|
||||
: 'warning'
|
||||
"
|
||||
>
|
||||
{{ scope.row[scope.prop] === null ? "未知" : scope.row[scope.prop] == 1 ? "男" : "女" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
<template #user="scope">
|
||||
<div class="flex align-center">
|
||||
<el-avatar :src="scope.row.headImg" />
|
||||
<el-tag>{{ scope.row.nickName }}</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<template #link="scope">
|
||||
<el-link>{{ scope.row[scope.prop] }}</el-link>
|
||||
</template>
|
||||
<template #mobile="scope">
|
||||
<el-text>{{ scope.row[scope.prop] }}</el-text>
|
||||
<copy-button
|
||||
v-if="scope.row[scope.prop]"
|
||||
:text="scope.row[scope.prop]"
|
||||
style="margin-left: 2px"
|
||||
/>
|
||||
</template>
|
||||
</page-content>
|
||||
|
||||
<!-- 新增 -->
|
||||
<page-modal
|
||||
ref="addModalRef"
|
||||
:modal-config="addModalConfig"
|
||||
@submit-click="handleSubmitClick"
|
||||
></page-modal>
|
||||
|
||||
<!-- 编辑 -->
|
||||
<page-modal
|
||||
ref="editModalRef"
|
||||
:modal-config="editModalConfig"
|
||||
@submit-click="handleSubmitClick"
|
||||
></page-modal>
|
||||
<!-- 用户余额修改 -->
|
||||
<page-modal
|
||||
ref="editMoneyModalRef"
|
||||
:modal-config="editMoneyModalConfig"
|
||||
@formDataChange="formDataChange"
|
||||
@submit-click="handleSubmitClick"
|
||||
></page-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup >
|
||||
import usePage from "@/components/CURD/usePage";
|
||||
import addModalConfig from "./config/add";
|
||||
import contentConfig from "./config/content";
|
||||
import editModalConfig from "./config/edit";
|
||||
import editMoneyModalConfig, { addOptions, reduceOptions } from "./config/edit-money";
|
||||
import searchConfig from "./config/search";
|
||||
import { returnOptionsLabel } from "./config/config";
|
||||
const editMoneyModalRef = ref(null);
|
||||
const {
|
||||
searchRef,
|
||||
contentRef,
|
||||
addModalRef,
|
||||
editModalRef,
|
||||
handleQueryClick,
|
||||
handleResetClick,
|
||||
// handleAddClick,
|
||||
// handleEditClick,
|
||||
handleSubmitClick,
|
||||
handleExportClick,
|
||||
handleSearchClick,
|
||||
handleFilterChange,
|
||||
} = usePage();
|
||||
|
||||
// 修改金额表单类型
|
||||
function formDataChange(type, val) {
|
||||
if (type == "type") {
|
||||
if (val == 1) {
|
||||
editMoneyModalConfig.formItems[4].options = addOptions;
|
||||
} else {
|
||||
editMoneyModalConfig.formItems[4].options = reduceOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增
|
||||
async function handleAddClick() {
|
||||
addModalRef.value?.setModalVisible();
|
||||
// 加载部门下拉数据源
|
||||
// addModalConfig.formItems[2]!.attrs!.data = await DeptAPI.getOptions();
|
||||
// 加载角色下拉数据源
|
||||
// addModalConfig.formItems[4]!.options = await RoleAPI.getOptions();
|
||||
}
|
||||
// 编辑
|
||||
async function handleEditClick(row) {
|
||||
editModalRef.value?.handleDisabled(false);
|
||||
editModalRef.value?.setModalVisible();
|
||||
// 根据id获取数据进行填充
|
||||
// const data = await VersionApi.getFormData(row.id);
|
||||
editModalRef.value?.setFormData({ ...row, headImg: row.headImg ? [row.headImg] : "" });
|
||||
}
|
||||
|
||||
// 其他工具栏
|
||||
function handleToolbarClick(name) {
|
||||
console.log(name);
|
||||
if (name === "custom1") {
|
||||
ElMessage.success("点击了自定义1按钮");
|
||||
}
|
||||
}
|
||||
|
||||
// 其他操作列
|
||||
async function handleOperatClick(data) {
|
||||
const row = data.row;
|
||||
if (data.name == "more") {
|
||||
if (data.command === "change-money") {
|
||||
editMoneyModalRef.value.setModalVisible();
|
||||
editMoneyModalRef.value.setFormData({ ...row, headImg: row.headImg ? [row.headImg] : "" });
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 切换示例
|
||||
const isA = ref(true);
|
||||
</script>
|
||||
<style scoped>
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user