大改:重新整理菜单及权限

This commit is contained in:
2024-07-19 08:45:00 +08:00
parent 81006e7636
commit 48ea6d0301
15 changed files with 519 additions and 366 deletions

View File

@@ -1,7 +1,7 @@
VITE_APP_NAME=后台管理系统 VITE_APP_NAME=后台管理系统
VITE_SOCKET_URL=wss://server.gxwebsoft.com VITE_SOCKET_URL=wss://server.gxwebsoft.com
VITE_SERVER_URL=https://server.gxwebsoft.com/api #VITE_SERVER_URL=https://server.gxwebsoft.com/api
VITE_API_URL=https://modules.gxwebsoft.com/api VITE_API_URL=https://modules.gxwebsoft.com/api
#VITE_SERVER_URL=http://127.0.0.1:9091/api VITE_SERVER_URL=http://127.0.0.1:9091/api
#VITE_API_URL=http://127.0.0.1:9099/api #VITE_API_URL=http://127.0.0.1:9099/api

View File

@@ -160,4 +160,5 @@ export interface AppParam extends PageParam {
showIndex?: boolean; showIndex?: boolean;
showExpiration?: boolean; showExpiration?: boolean;
keywords?: any; keywords?: any;
sceneType?: string;
} }

View File

@@ -39,5 +39,8 @@ export interface TenantParam extends PageParam {
appId?: string; appId?: string;
companyId?: number; companyId?: number;
companyName?: string; companyName?: string;
version?: string;
province?: string;
city?: string;
tenantCode?: string; tenantCode?: string;
} }

View File

@@ -55,7 +55,7 @@
ColumnItem, ColumnItem,
DatasourceFunction DatasourceFunction
} from 'ele-admin-pro/es/ele-pro-table/types'; } from 'ele-admin-pro/es/ele-pro-table/types';
import { pageTenant } from '@/api/system/tennat'; import { pageTenant } from '@/api/system/tenant';
import { FILE_THUMBNAIL } from '@/config/setting'; import { FILE_THUMBNAIL } from '@/config/setting';
import { EleProTable } from 'ele-admin-pro'; import { EleProTable } from 'ele-admin-pro';
import { Company, CompanyParam } from '@/api/system/company/model'; import { Company, CompanyParam } from '@/api/system/company/model';

View File

@@ -26,7 +26,7 @@
import { BulbOutlined } from '@ant-design/icons-vue'; import { BulbOutlined } from '@ant-design/icons-vue';
import { ref } from 'vue'; import { ref } from 'vue';
import SelectData from './components/select-data.vue'; import SelectData from './components/select-data.vue';
import { Tenant } from '@/api/system/tennat/model'; import { Tenant } from '@/api/system/tenant/model';
withDefaults( withDefaults(
defineProps<{ defineProps<{

View File

@@ -6,7 +6,7 @@ import { formatMenus, toTreeData, formatTreeData } from 'ele-admin-pro';
import type { MenuItem } from 'ele-admin-pro'; import type { MenuItem } from 'ele-admin-pro';
import { USER_MENUS } from '@/config/setting'; import { USER_MENUS } from '@/config/setting';
import { getTenantInfo } from '@/api/layout'; import { getTenantInfo } from '@/api/layout';
import { Tenant } from '@/api/system/tennat/model'; import { Tenant } from '@/api/system/tenant/model';
import { Company } from '@/api/system/company/model'; import { Company } from '@/api/system/company/model';
// const EXTRA_MENUS: any = []; // const EXTRA_MENUS: any = [];

View File

@@ -7,7 +7,7 @@ import type { MenuItemType } from 'ele-admin-pro/es';
import type { User } from '@/api/system/user/model'; import type { User } from '@/api/system/user/model';
import { TOKEN_STORE_NAME, USER_MENUS } from '@/config/setting'; import { TOKEN_STORE_NAME, USER_MENUS } from '@/config/setting';
import { getUserInfo } from '@/api/layout'; import { getUserInfo } from '@/api/layout';
import { initialization } from '@/api/system/tennat'; import { initialization } from '@/api/system/tenant';
// import { isExternalLink } from 'ele-admin-pro'; // import { isExternalLink } from 'ele-admin-pro';
// import { message } from 'ant-design-vue'; // import { message } from 'ant-design-vue';
const EXTRA_MENUS: any = []; const EXTRA_MENUS: any = [];

View File

@@ -3,7 +3,6 @@
<div style="display: flex; justify-content: space-between"> <div style="display: flex; justify-content: space-between">
<a-space :size="10" style="flex-wrap: wrap; margin-right: 20px"> <a-space :size="10" style="flex-wrap: wrap; margin-right: 20px">
<a-button <a-button
v-permission="['sys:company:save']"
type="primary" type="primary"
class="ele-btn-icon" class="ele-btn-icon"
@click="add" @click="add"
@@ -39,6 +38,7 @@
import useSearch from '@/utils/use-search'; import useSearch from '@/utils/use-search';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import { CompanyParam } from '@/api/system/company/model'; import { CompanyParam } from '@/api/system/company/model';
import { openNew } from "@/utils/common";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -75,7 +75,8 @@
// 新增 // 新增
const add = () => { const add = () => {
emit('add'); // emit('add');
openNew(`https://www.gxwebsoft.com/login`);
}; };
/* 搜索 */ /* 搜索 */

View File

@@ -21,14 +21,27 @@
</template> </template>
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
<template v-if="column.key === 'shortName'"> <template v-if="column.key === 'shortName'">
<div class="flex flex-row">
<StarOutlined <StarOutlined
@click="onCollection(record)" @click="onCollection(record)"
class="tag-icon" class="tag-icon mt-1"
:class=" :class="
record.collection ? 'ele-text-warning' : 'ele-text-placeholder' record.collection
? 'ele-text-warning'
: 'ele-text-placeholder'
" "
/> />
<div class="tenant-name-box flex flex-col">
<a
:href="`https://${record.tenantId}.websoft.top`"
target="_blank"
class="tenant-name"
>
{{ record.shortName }} {{ record.shortName }}
</a>
<span class="text-gray-400">{{ record.comments }}</span>
</div>
</div>
</template> </template>
<template v-if="column.key === 'companyLogo'"> <template v-if="column.key === 'companyLogo'">
<div class="company-box"> <div class="company-box">
@@ -46,11 +59,11 @@
<div <div
v-any-role="['merchant', 'superAdmin']" v-any-role="['merchant', 'superAdmin']"
class="ele-text-secondary" class="ele-text-secondary"
>{{ record.phone }}</div >{{ record.phone }}
> </div>
<div v-role="'guest'" class="ele-text-secondary">{{ <div v-role="'guest'" class="ele-text-secondary"
record.mobile >{{ record.mobile }}
}}</div> </div>
</template> </template>
<template v-if="column.key === 'comments'"> <template v-if="column.key === 'comments'">
<span class="ele-text-secondary">{{ record.comments }}</span> <span class="ele-text-secondary">{{ record.comments }}</span>
@@ -64,7 +77,7 @@
</template> </template>
</template> </template>
<template v-if="column.key === 'expirationTime'"> <template v-if="column.key === 'expirationTime'">
<template v-if="record.version == 30"> - </template> <template v-if="record.version == 30"> -</template>
<template v-else> <template v-else>
<div>{{ toDateString(record.createTime, 'yyyy-MM-dd') }}</div> <div>{{ toDateString(record.createTime, 'yyyy-MM-dd') }}</div>
<div>{{ toDateString(record.expirationTime, 'yyyy-MM-dd') }}</div> <div>{{ toDateString(record.expirationTime, 'yyyy-MM-dd') }}</div>
@@ -86,142 +99,33 @@
</template> </template>
</template> </template>
<template v-if="column.key === 'version'"> <template v-if="column.key === 'version'">
<a-tag v-if="record.version == 10" <span v-if="record.version == 10"> 免费版 </span>
><IdcardOutlined class="tag-icon" />免费版</a-tag <span v-if="record.version == 20"> 商业版 </span>
> <span v-if="record.version == 30"> 永久授权 </span>
<a-tag color="blue" v-if="record.version == 20"
><IdcardOutlined class="tag-icon" />商业版</a-tag
>
<a-tag color="cyan" v-if="record.version == 30"
><IdcardOutlined class="tag-icon" />永久授权</a-tag
>
</template> </template>
<!-- <template v-if="column.key === 'version'">-->
<!-- <a-button-->
<!-- :size="`small`"-->
<!-- v-if="record.version == 10"-->
<!-- style="-->
<!-- background-color: var(&#45;&#45;grey-8);-->
<!-- border: 1px solid var(&#45;&#45;grey-5);-->
<!-- color: var(&#45;&#45;grey-3);-->
<!-- "-->
<!-- ><IdcardOutlined />体验版</a-button-->
<!-- >-->
<!-- <a-button-->
<!-- :size="`small`"-->
<!-- style="-->
<!-- background-color: var(&#45;&#45;green-1);-->
<!-- border: 1px solid var(&#45;&#45;green-5);-->
<!-- color: var(&#45;&#45;green-7);-->
<!-- "-->
<!-- v-if="record.version == 20"-->
<!-- ><IdcardOutlined />商业版</a-button-->
<!-- >-->
<!-- <a-button-->
<!-- :size="`small`"-->
<!-- style="-->
<!-- background-color: var(&#45;&#45;cyan-1);-->
<!-- border: 1px solid var(&#45;&#45;cyan-5);-->
<!-- color: var(&#45;&#45;cyan-7);-->
<!-- "-->
<!-- v-if="record.version == 30"-->
<!-- ><IdcardOutlined />永久授权</a-button-->
<!-- >-->
<!-- </template>-->
<template v-if="column.key === 'appUrl'"> <template v-if="column.key === 'appUrl'">
<template v-if="record.adminUrl"> <DesktopOutlined
<a-popover> @click="openNew(`https://${record.tenantId}.websoft.top`)"
<template #content> />
<div class="qrcode">
<a
@click="openNew(record.adminUrl)"
class="ele-text-heading"
>进入后台管理系统</a
>
<div class="ele-text-placeholder">
账号密码:预留手机号+短信验证码登录
</div>
<div
@click="copyText(`${record.adminUrl}`)"
class="ele-text-secondary"
>{{ record.adminUrl }}</div
>
</div>
</template>
<a-button :size="`small`" @click="openNew(record.adminUrl)"
><DesktopOutlined />管理后台</a-button
>
</a-popover>
</template>
<template v-if="record.merchantUrl">
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popover> <GlobalOutlined
<template #content> @click="openNew(`https://${record.tenantCode}.wsdns.cn`)"
<div class="qrcode"> />
<a
@click="openNew(record.merchantUrl)"
class="ele-text-heading"
>访问商户端</a
>
<div class="ele-text-placeholder"
>商户及登录账号在总后台创建</div
>
<div
@click="copyText(`${record.merchantUrl}`)"
class="ele-text-secondary"
>{{ record.merchantUrl }}</div
>
</div>
</template>
<a-button :size="`small`" @click="openNew(record.merchantUrl)"
><DesktopOutlined />商户端</a-button
>
</a-popover>
</template>
<template v-if="record.websiteUrl">
<a-divider type="vertical" />
<a-popover>
<template #content>
<div class="qrcode">
<a
class="ele-text-heading"
@click="openNew(record.websiteUrl)"
>访问PC端</a
>
<div
@click="copyText(`${record.websiteUrl}`)"
class="ele-text-secondary"
>{{ record.websiteUrl }}</div
>
</div>
</template>
<a-button :size="`small`" @click="openNew(record.websiteUrl)"
><LaptopOutlined class="ele-text-primary" />PC</a-button
>
</a-popover>
</template>
<template v-if="record.h5Code"> <template v-if="record.h5Code">
<a-divider type="vertical" /> <a-divider type="vertical" />
<a-popover> <a-popover>
<template #content> <template #content>
<div class="qrcode"> <div class="qrcode">
<ele-qr-code <ele-qr-code
:value="`http://${record.h5Code}`" :value="`https://${record.tenantCode}.wsdns.cn`"
:size="160" :size="160"
level="M" level="M"
:margin="1" :margin="1"
/> />
<span>手机扫一扫</span> <span>手机扫一扫</span>
<div
@click="copyText(`${record.h5Code}`)"
class="ele-text-secondary"
>{{ record.h5Code }}</div
>
</div> </div>
</template> </template>
<a-button :size="`small`" @click="openNew(record.h5Code)" <TabletOutlined />
><QrcodeOutlined class="ele-text-warning" />H5</a-button
>
</a-popover> </a-popover>
</template> </template>
<template v-if="record.mpWeixinCode"> <template v-if="record.mpWeixinCode">
@@ -238,11 +142,7 @@
微信扫一扫 微信扫一扫
</div> </div>
</template> </template>
<a-button :size="`small`" <WechatOutlined class="ele-text-success" />
><WechatOutlined
class="ele-text-success"
/>微信小程序</a-button
>
</a-popover> </a-popover>
</template> </template>
<template v-if="record.mpAlipayCode"> <template v-if="record.mpAlipayCode">
@@ -259,30 +159,12 @@
支付宝扫一扫 支付宝扫一扫
</div> </div>
</template> </template>
<a-button :size="`small`" <a-button :size="`small`">
><AlipayOutlined class="ele-text-primary" />支付宝</a-button <AlipayOutlined class="ele-text-primary" />
> 支付宝
</a-button>
</a-popover> </a-popover>
</template> </template>
<!-- <a-divider type="vertical" />-->
<!-- <a-dropdown>-->
<!-- <template #overlay>-->
<!-- <a-menu>-->
<!-- <a-menu-item key="1" @click="onCode(record.iosUrl)">-->
<!-- <AndroidOutlined class="tag-icon" />-->
<!-- <span>APP端</span>-->
<!-- </a-menu-item>-->
<!-- <a-menu-item key="2" @click="openNew(record.android)">-->
<!-- <AlipayOutlined class="tag-icon" />-->
<!-- <span>支付宝</span>-->
<!-- </a-menu-item>-->
<!-- </a-menu>-->
<!-- </template>-->
<!-- <a-button :size="`small`">-->
<!-- 更多-->
<!-- <DownOutlined />-->
<!-- </a-button>-->
<!-- </a-dropdown>-->
</template> </template>
<template v-if="column.key === 'action'"> <template v-if="column.key === 'action'">
<template v-if="record.deleted == 1"> <template v-if="record.deleted == 1">
@@ -327,7 +209,10 @@
import { message, Modal } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { import {
ExclamationCircleOutlined, ExclamationCircleOutlined,
IdcardOutlined IdcardOutlined,
TabletOutlined,
DesktopOutlined,
GlobalOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro'; import type { EleProTable } from 'ele-admin-pro';
import type { import type {
@@ -345,6 +230,7 @@
import { Company, CompanyParam } from '@/api/system/company/model'; import { Company, CompanyParam } from '@/api/system/company/model';
import { toDateString } from 'ele-admin-pro'; import { toDateString } from 'ele-admin-pro';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
const { currentRoute } = useRouter(); const { currentRoute } = useRouter();
import { pageCompanyAll } from '@/api/system/company'; import { pageCompanyAll } from '@/api/system/company';
import { import {
@@ -370,44 +256,27 @@
title: '租户ID', title: '租户ID',
fixed: 'left', fixed: 'left',
dataIndex: 'tenantId', dataIndex: 'tenantId',
align: 'center',
width: 90 width: 90
}, },
{ {
title: 'Logo', title: 'Logo',
dataIndex: 'companyLogo', dataIndex: 'companyLogo',
key: 'companyLogo', key: 'companyLogo',
width: 90 align: 'center',
width: 180
}, },
{ {
title: '租户名称', title: '租户名称',
dataIndex: 'shortName', dataIndex: 'shortName',
key: 'shortName', key: 'shortName'
width: 240
},
{
title: '应用入口',
key: 'appUrl'
}, },
{ {
title: '版本', title: '版本',
dataIndex: 'version', dataIndex: 'version',
align: 'center', align: 'center',
width: 160,
key: 'version' key: 'version'
}, },
{
title: '客户信息',
dataIndex: 'phone',
align: 'center',
key: 'phone',
width: 160
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
width: 200
},
{ {
title: '到期时间', title: '到期时间',
dataIndex: 'expirationTime', dataIndex: 'expirationTime',
@@ -417,6 +286,12 @@
sorter: true, sorter: true,
customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd') customRender: ({ text }) => toDateString(text, 'yyyy-MM-dd')
}, },
{
title: '操作',
align: 'center',
key: 'appUrl',
width: 180
},
// { // {
// title: '状态', // title: '状态',
// dataIndex: 'status', // dataIndex: 'status',
@@ -425,12 +300,6 @@
// key: 'status', // key: 'status',
// customRender: ({ text }) => ['已上线', '开发中'][text] // customRender: ({ text }) => ['已上线', '开发中'][text]
// } // }
{
title: '操作',
key: 'action',
width: 180,
align: 'center'
}
]); ]);
// 表格选中数据 // 表格选中数据
@@ -617,6 +486,7 @@
.tag-icon { .tag-icon {
padding-right: 6px; padding-right: 6px;
} }
.qrcode { .qrcode {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -1,11 +1,45 @@
<template> <template>
<a-card <a-card
style="background-color: transparent" style="background-color: transparent"
:bordered="false"
:body-style="{ padding: '0px' }" :body-style="{ padding: '0px' }"
> >
<div class="card-title"> <a-alert
<a-typography-title :level="3">应用模块</a-typography-title> message="欢迎使用"
description="请选择需要安装的插件"
type="success"
show-icon
closable
/>
<div style="margin: 30px auto; display: flex; justify-content: center">
<a-space style="flex-wrap: wrap">
<industry-select
v-model:value="industry"
valueField="label"
size="large"
placeholder="按行业筛选"
class="ele-fluid"
@change="onIndustry"
/>
<a-input-search
allow-clear
size="large"
style="width: 360px"
placeholder="请输入搜索关键词"
v-model:value="where.keywords"
@pressEnter="reload"
@search="reload"
/>
<a-button size="large" @click="reset">重置</a-button>
</a-space>
</div> </div>
<a-tabs v-model:active-key="where.sceneType" @change="onTabs">
<a-tab-pane tab="热门推荐" key="recommend" />
<a-tab-pane tab="免费热榜" key="free" />
<a-tab-pane tab="付费热榜" key="pay" />
<a-tab-pane tab="最新上架" key="new" />
<a-tab-pane tab="我的收藏" key="collect" />
</a-tabs>
<a-row :gutter="16"> <a-row :gutter="16">
<a-col <a-col
v-bind=" v-bind="
@@ -18,7 +52,8 @@
:key="index" :key="index"
:span="6" :span="6"
> >
<a-card class="gutter-box" hoverable> <a-card hoverable>
<div class="gutter-box">
<div class="plug-item"> <div class="plug-item">
<a-image <a-image
:height="80" :height="80"
@@ -54,56 +89,29 @@
</div> </div>
<div class="plug-bottom"> <div class="plug-bottom">
<div class="downloads ele-text-placeholder" <div class="downloads ele-text-placeholder"
>安装 {{ item.clicks }}</div >安装 {{ item.clicks }}
> </div>
<a-button type="primary" disabled v-if="planId === item.tenantId" <a-button type="primary" disabled v-if="planId === item.tenantId"
>已安装</a-button >已安装
> </a-button>
<a-button v-else type="primary" @click="onClone(item)" <a-button v-else type="primary" @click="onClone(item)"
>安装</a-button >安装
> </a-button>
</div>
</div> </div>
</a-card> </a-card>
<!-- <a-card class="gutter-box" hoverable>-->
<!-- <div class="flex-justify">-->
<!-- <div class="plug-item">-->
<!-- <a-image-->
<!-- :height="80"-->
<!-- :width="80"-->
<!-- :preview="false"-->
<!-- :src="item.companyLogo"-->
<!-- fallback="https://file.wsdns.cn/20230218/550e610d43334dd2a7f66d5b20bd58eb.svg"-->
<!-- />-->
<!-- <div class="info">-->
<!-- <span class="name ele-text-heading">{{ item.tenantName }}</span>-->
<!-- <a-rate class="rate" v-model:value="rate" disabled allow-half />-->
<!-- <div class="company ele-text-placeholder">-->
<!-- <a-typography-text-->
<!-- type="secondary"-->
<!-- :ellipsis="{ rows: 1, expandable: true, symbol: '..' }"-->
<!-- >-->
<!-- {{ item.companyName }}-->
<!-- </a-typography-text>-->
<!-- </div>-->
<!-- <div class="plug-desc ele-text-secondary">-->
<!-- <a-typography-paragraph-->
<!-- type="secondary"-->
<!-- :ellipsis="{ rows: 2, expandable: true, symbol: '显示' }"-->
<!-- :content="item.comments"-->
<!-- />-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="plug-desc ele-text-secondary">-->
<!-- <a-button-->
<!-- @click="openUrl('/system/plug/detail?id=' + item.companyId)"-->
<!-- >去开通</a-button-->
<!-- >-->
<!-- </div>-->
<!-- </div>-->
<!-- </a-card>-->
</a-col> </a-col>
</a-row> </a-row>
<div class="plug-page" v-if="list.length">
<a-pagination
v-model:current="where.page"
v-model:pageSize="where.limit"
size="large"
:total="total"
@change="reload"
/>
</div>
<a-empty v-else />
</a-card> </a-card>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -116,6 +124,7 @@
import useSearch from '@/utils/use-search'; import useSearch from '@/utils/use-search';
import { message } from 'ant-design-vue/es'; import { message } from 'ant-design-vue/es';
import { pageCompanyAll } from '@/api/system/company'; import { pageCompanyAll } from '@/api/system/company';
import { pagePlug } from "@/api/system/plug";
const props = defineProps<{ const props = defineProps<{
// 修改回显的数据 // 修改回显的数据
@@ -129,85 +138,59 @@
const searchText = ref(''); const searchText = ref('');
const rate = ref(3.5); const rate = ref(3.5);
const list = ref<Company[]>([]); const list = ref<Company[]>([]);
const industry = ref<any>();
const total = ref<any>(0);
const planId = ref<number>(Number(localStorage.getItem('PlanId')));
// 查询条件 // 查询条件
const { where, resetFields } = useSearch<CompanyParam>({ const { where, resetFields } = useSearch<CompanyParam>({
keywords: undefined, keywords: undefined,
limit: 4, industryParent: '',
recommend: true, industryChild: '',
authoritative: 1 recommend: undefined,
authoritative: 1,
sceneType: 'recommend',
limit: 20,
page: 1
}); });
const reload = () => { const onIndustry = (item: any) => {
resetFields(); where.industryChild = item[1];
if (searchText.value) { };
where.recommend = undefined;
where.keywords = searchText.value; const onTabs = (index) => {
if (index == 'recommend') {
where.recommend = true;
} }
if (props.use) { if (index == 'free') {
where.recommend = false; where.recommend = false;
} }
if (index == 'pay') {
where.recommend = false;
}
if (index == 'new') {
where.sceneType = 'new';
}
if (index == 'collect') {
where.sceneType = 'collect';
}
reload();
};
const reset = () => {
resetFields();
reload();
};
const reload = () => {
where.sort = 'buys'; where.sort = 'buys';
where.order = 'desc'; where.order = 'desc';
pagePlug({}).then((data) => {
const hide = message.loading('加载中...'); total.value = data?.count;
pageCompanyAll(where)
.then((data) => {
if (data?.list) { if (data?.list) {
list.value = data?.list; list.value = data?.list;
} }
})
.finally(() => {
hide();
}); });
}; };
reload(); reload();
</script> </script>
<style scoped lang="less">
.ele-body-card {
background-color: transparent;
padding: 20px;
}
.card-title {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.gutter-row {
margin: 0 auto 30px auto;
.gutter-box {
.plug-item {
display: flex;
.info {
font-size: 14px;
margin-left: 6px;
.name {
font-size: 20px;
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
font-weight: 500;
}
.rate {
font-size: 13px;
}
.company {
}
}
}
.plug-desc {
padding: 10px 0;
font-size: 16px;
min-height: 88px;
}
.plug-bottom {
display: flex;
justify-content: space-between;
}
}
}
</style>

View File

@@ -89,14 +89,14 @@
</div> </div>
<div class="plug-bottom"> <div class="plug-bottom">
<div class="downloads ele-text-placeholder" <div class="downloads ele-text-placeholder"
>安装 {{ item.clicks }}</div >安装 {{ item.clicks }}
> </div>
<a-button type="primary" disabled v-if="planId === item.tenantId" <a-button type="primary" disabled v-if="planId === item.tenantId"
>已安装</a-button >已安装
> </a-button>
<a-button v-else type="primary" @click="onClone(item)" <a-button v-else type="primary" @click="onClone(item)"
>安装</a-button >安装
> </a-button>
</div> </div>
</div> </div>
</a-card> </a-card>
@@ -204,18 +204,23 @@
justify-content: space-between; justify-content: space-between;
margin-top: 10px; margin-top: 10px;
} }
.gutter-row { .gutter-row {
margin: 0 auto 30px auto; margin: 0 auto 30px auto;
.gutter-box { .gutter-box {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
min-height: 200px; min-height: 200px;
.plug-item { .plug-item {
display: flex; display: flex;
.info { .info {
font-size: 14px; font-size: 14px;
margin-left: 6px; margin-left: 6px;
.name { .name {
font-size: 20px; font-size: 20px;
display: -webkit-box; display: -webkit-box;
@@ -225,28 +230,34 @@
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
font-weight: 500; font-weight: 500;
} }
.rate { .rate {
font-size: 13px; font-size: 13px;
} }
.company { .company {
} }
} }
} }
.plug-desc { .plug-desc {
padding: 10px 0; padding: 10px 0;
font-size: 16px; font-size: 16px;
} }
.plug-bottom { .plug-bottom {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
} }
} }
.ele-text-heading { .ele-text-heading {
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.plug-page { .plug-page {
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@@ -12,22 +12,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import Tenant from './components/tenant.vue'; import Tenant from './components/tenant.vue';
// import Plug from './components/plug.vue';
// const active = ref('list');
</script> </script>
<script lang="ts"> <script lang="ts">
import * as PlugIcons from '@/layout/menu-icons';
export default { export default {
name: 'SystemPlug', name: 'SystemPlug'
components: PlugIcons
}; };
</script> </script>
<style lang="less" scoped>
.ele-body-card {
background-color: transparent;
padding: 20px;
}
</style>

View File

@@ -0,0 +1,297 @@
<template>
<div class="ele-body">
<a-card :bordered="false">
<!-- 表格 -->
<ele-pro-table
ref="tableRef"
row-key="logId"
:columns="columns"
:datasource="datasource"
v-model:selection="selection"
:scroll="{ x: 1000 }"
:where="defaultWhere"
cache-key="userBalanceLogTable"
>
<template #toolbar>
<a-space>
<a-button
danger
type="primary"
class="ele-btn-icon"
@click="removeBatch"
>
<template #icon>
<delete-outlined />
</template>
<span>批量删除</span>
</a-button>
<a-range-picker
v-model:value="dateRange"
value-format="YYYY-MM-DD"
class="ele-fluid"
/>
<a-input-search
allow-clear
v-model:value="searchText"
placeholder="请输入关键词"
@search="reload"
@pressEnter="reload"
@close="onClose"
/>
<a-button @click="reset">重置</a-button>
</a-space>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'nickname'">
<a-tooltip :title="`用户ID${record.userId}`">
<a @click="onSearch(record)">{{ record.nickname }}</a>
</a-tooltip>
</template>
<template v-if="column.key === 'scene'">
<a-tag v-if="record.scene === 10"> 用户充值 </a-tag>
<a-tag v-if="record.scene === 20"> 用户消费 </a-tag>
<a-tag v-if="record.scene === 30"> 管理员操作 </a-tag>
<a-tag v-if="record.scene === 40"> 订单退款 </a-tag>
</template>
<template v-if="column.key === 'money'">
<span
class="ele-text-success"
v-if="record.scene === 10 || record.scene === 40"
>
+{{ formatNumber(record.money) }}
</span>
<template v-else-if="record.scene === 30">
<span v-if="record.money > 0" class="ele-text-success">
+{{ formatNumber(record.money) }}
</span>
<span v-else class="ele-text-danger">
-{{ formatNumber(record.money * -1) }}
</span>
</template>
<span class="ele-text-danger" v-else>
-{{ formatNumber(record.money) }}
</span>
</template>
<template v-if="column.key === 'balance'">
<span> {{ formatNumber(record.balance) }} </span>
</template>
<!-- <template v-else-if="column.key === 'status'">-->
<!-- <a-switch-->
<!-- :checked="record.status === 0"-->
<!-- @change="(checked: boolean) => editStatus(checked, record)"-->
<!-- />-->
<!-- </template>-->
<!-- <template v-else-if="column.key === 'action'">-->
<!-- <a-space>-->
<!-- <a @click="openEdit(record)">修改</a>-->
<!-- <a-divider type="vertical" />-->
<!-- <a @click="resetPsw(record)">重置密码</a>-->
<!-- <a-divider type="vertical" />-->
<!-- <a-popconfirm-->
<!-- placement="topRight"-->
<!-- title="确定要删除此用户吗?"-->
<!-- @confirm="remove(record)"-->
<!-- >-->
<!-- <a class="ele-text-danger">删除</a>-->
<!-- </a-popconfirm>-->
<!-- </a-space>-->
<!-- </template>-->
</template>
</ele-pro-table>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref, reactive } from 'vue';
import { message, Modal } from 'ant-design-vue/es';
import {
DeleteOutlined,
ExclamationCircleOutlined
} from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro/es';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import {
toDateString,
messageLoading,
formatNumber
} from 'ele-admin-pro/es';
import {
pageUserBalanceLog,
removeUserBalanceLog,
removeUserBalanceLogs
} from '@/api/user/balance-log';
import {
UserBalanceLog,
UserBalanceLogParam
} from '@/api/user/balance-log/model';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '用户昵称',
key: 'nickname',
dataIndex: 'nickname',
showSorterTooltip: false
},
{
title: '场景',
dataIndex: 'scene',
key: 'scene',
align: 'center',
showSorterTooltip: false,
filters: [
{ text: '用户充值', value: 10 },
{ text: '用户消费', value: 20 },
{ text: '管理员操作', value: 30 },
{ text: '订单退款', value: 40 }
]
},
{
title: '变动金额',
dataIndex: 'money',
key: 'money',
sorter: true,
showSorterTooltip: false
},
{
title: '账户金额',
dataIndex: 'balance',
key: 'balance',
sorter: true,
showSorterTooltip: false
},
{
title: '管理员备注',
dataIndex: 'remark'
},
{
title: '描述/说明',
dataIndex: 'comments'
},
{
title: '时间',
dataIndex: 'createTime',
sorter: true,
showSorterTooltip: false,
ellipsis: true,
customRender: ({ text }) => toDateString(text)
}
]);
// 表格选中数据
const selection = ref<UserBalanceLog[]>([]);
const searchText = ref('');
const userId = ref<number>(0);
// 日期范围选择
const dateRange = ref<[string, string]>(['', '']);
// 默认搜索条件
const defaultWhere = reactive({
username: '',
nickname: '',
userId: undefined
});
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
const [d1, d2] = dateRange.value ?? [];
where = {
...{
createTimeStart: d1 ? d1 + ' 00:00:00' : '',
createTimeEnd: d2 ? d2 + ' 23:59:59' : ''
}
};
if (userId.value) {
where.userId = userId.value;
}
if (filters) {
where.sceneMultiple = filters.scene;
}
where.keywords = searchText.value;
return pageUserBalanceLog({ ...where, ...orders, page, limit });
};
// 按用户搜索
const onSearch = (record) => {
userId.value = record?.userId;
searchText.value = record.nickname;
tableRef?.value?.reload();
};
const reset = () => {
userId.value = 0;
searchText.value = '';
reload();
};
/* 搜索 */
const reload = (where?: UserBalanceLogParam) => {
selection.value = [];
tableRef?.value?.reload({ page: 1, where });
};
/* 删除单个 */
const remove = (row: UserBalanceLog) => {
const hide = messageLoading('请求中..', 0);
removeUserBalanceLog(row.userId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = messageLoading('请求中..', 0);
removeUserBalanceLogs(selection.value.map((d) => d.logId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
</script>
<script lang="ts">
export default {
name: 'SystemUser'
};
</script>

View File

@@ -411,7 +411,6 @@
where = {}; where = {};
where.roleId = filters.roles; where.roleId = filters.roles;
where.keywords = searchText.value; where.keywords = searchText.value;
where.isAdmin = 0;
return pageUsers({ page, limit, ...where, ...orders }); return pageUsers({ page, limit, ...where, ...orders });
}; };